最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis中原子性操作的的實現(xiàn)

 更新時間:2025年11月18日 09:53:51   作者:祈禱蒼天賜我java之術  
本文主要介紹了Redis的原子性操作,包括其單線程模型和命令隊列機制,原子性的邊界,及通過事務和Lua腳本實現(xiàn)多命令的原子性,具有一定的參考價值,感興趣的可以了解一下

一、Redis 原子性操作的本質(zhì):為什么 Redis 能保證原子性?

首先需要明確一個關鍵概念:Redis 的原子性是指單個命令的執(zhí)行是 "不可中斷" 的—— 當一個命令開始執(zhí)行后,直到其執(zhí)行完畢,Redis 不會中斷它去執(zhí)行其他命令。這種特性并非 Redis 獨創(chuàng),而是基于其單線程模型的天然優(yōu)勢。

1.1 底層原理:單線程模型 + 命令隊列

Redis 采用單線程事件循環(huán)模型處理客戶端請求,這種設計架構主要由以下幾個關鍵組件構成:

  1. I/O 多路復用:Redis 使用 epoll/kqueue/select 等系統(tǒng)調(diào)用來高效處理大量網(wǎng)絡連接
  2. 命令隊列:所有客戶端請求都會被序列化到一個全局內(nèi)存隊列中
  3. 單線程事件循環(huán):主線程按 "先進先出(FIFO)" 的順序從隊列中取出命令執(zhí)行

這種設計從根本上保證了:

  • 命令執(zhí)行的獨占性:每個命令在執(zhí)行期間獨占 CPU 資源
  • 狀態(tài)一致性:命令執(zhí)行的結果不會出現(xiàn) "部分完成" 的中間狀態(tài)
  • 操作完整性:完整的操作序列不會被其他命令打斷

典型應用場景示例: 當執(zhí)行 INCR key 命令時,Redis 會嚴格按照以下順序完整執(zhí)行:

  1. 從內(nèi)存中讀取 key 的當前值(假設為 5)
  2. 在 CPU 寄存器中執(zhí)行加 1 操作(5 → 6)
  3. 將新值(6)寫回內(nèi)存
  4. 返回結果給客戶端

在此期間,即使有 100 個客戶端同時發(fā)送 INCR 命令,Redis 也會將它們排隊處理,確保每個 INCR 操作都能正確累加。

1.2 原子性的邊界:單個命令 vs 多個命令

需要特別注意的是:Redis 僅保證 "單個命令" 的原子性,多個命令的組合并不天然具備原子性。理解這一點對設計可靠的 Redis 應用至關重要。

典型問題示例

# 以下兩個命令組合不具備原子性
GET key1  # 步驟1:讀取key1
SET key2 value2  # 步驟2:寫入key2

潛在風險場景

  1. 客戶端A執(zhí)行 GET key1 獲取值為 100
  2. 此時客戶端B修改了 key1 的值為 200
  3. 客戶端A繼續(xù)執(zhí)行 SET key2 value2
  4. 結果:客戶端A基于已過期的 key1 值做出了錯誤決策

解決方案對比

方案實現(xiàn)方式適用場景性能影響
事務(MULTI/EXEC)將多個命令打包執(zhí)行簡單的命令組合中等,需要排隊
Lua腳本原子執(zhí)行復雜邏輯需要條件判斷的業(yè)務較高,需要解析腳本
WATCH樂觀鎖機制需要檢測變化的場景較高,可能重試

Lua 腳本示例

-- 原子性地檢查并設置值
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("SET", KEYS[2], ARGV[2])
else
    return 0
end

最佳實踐建議

  1. 對于簡單的計數(shù)器場景,優(yōu)先使用原生原子命令(INCR/DECR 等)
  2. 需要組合不超過5個命令時,使用 MULTI/EXEC 事務
  3. 復雜業(yè)務邏輯(包含條件判斷)必須使用 Lua 腳本
  4. 對性能敏感的場景,提前測試不同方案的 QPS 表現(xiàn)

二、Redis 核心原子操作分類與實踐

2.1 基礎數(shù)據(jù)結構的原子操作

數(shù)據(jù)結構詳解與擴展應用場景

  1. String類型

    • SETNX 命令擴展應用:
      • 實現(xiàn)分布式鎖的基礎原語
      • 用戶首次登錄初始化配置
      • 防止緩存擊穿(當緩存失效時,只允許一個請求去查詢數(shù)據(jù)庫)
    • GETSET 典型使用場景:
      • 系統(tǒng)維護狀態(tài)切換(獲取當前狀態(tài)并更新為新狀態(tài))
      • 實現(xiàn)簡單的消息隊列(配合LPUSH使用)
  2. Hash類型

    • HSET 高級用法:
      • 用戶會話管理(存儲多個會話屬性)
      • 商品詳情緩存(避免序列化/反序列化整個對象)
    • HINCRBY 實際案例:
      • 電商平臺商品庫存扣減(保證庫存準確性)
      • 論壇帖子點贊計數(shù)
  3. List類型

    • 高級隊列模式:
      • 阻塞式隊列(BLPOP/BRPOP
      • 循環(huán)隊列(LINDEX+LPUSH
    • 典型應用:
      • 最新消息展示(固定長度列表)
      • 任務調(diào)度系統(tǒng)
  4. Set類型

    • 擴展功能:
      • 共同好友計算(SINTER
      • 數(shù)據(jù)去重處理
    • 實際案例:
      • 用戶標簽系統(tǒng)
      • 抽獎活動參與者管理
  5. ZSet類型

    • 高級應用:
      • 延遲隊列(使用時間戳作為score)
      • 熱點數(shù)據(jù)統(tǒng)計
    • 典型場景:
      • 游戲排行榜
      • 優(yōu)先級任務調(diào)度

用戶登錄狀態(tài)存儲的進階實現(xiàn)

// 高級登錄狀態(tài)管理
public boolean setLoginStatus(String userId, String deviceId) {
    String key = "user:" + userId + ":session";
    String value = deviceId + ":" + System.currentTimeMillis();
    
    // 使用SET命令的完整參數(shù)
    String result = jedis.set(key, value, 
        "NX",  // 僅當key不存在時設置
        "EX",  // 設置過期時間單位秒
        3600,  // 1小時過期
        "GET"  // 返回舊值(如果存在)
    );
    
    if (result != null) {
        // 處理舊設備踢出邏輯
        handleOldDeviceLogout(result);
    }
    return "OK".equals(result);
}

2.2 計數(shù)器與自增操作

INCR系列命令的底層原理

Redis實現(xiàn)原子自增的方式:

  1. 單線程模型保證命令串行執(zhí)行
  2. 內(nèi)存操作避免磁盤I/O延遲
  3. 特殊編碼優(yōu)化(當值較小時使用更緊湊的存儲格式)

計數(shù)器的高級應用模式

  1. 滑動窗口限流

    -- Lua腳本實現(xiàn)滑動窗口限流
    local current_time = redis.call('TIME')[1]
    local window_size = 60
    local max_requests = 100
    local key = KEYS[1]
    
    -- 清除過期記錄
    redis.call('ZREMRANGEBYSCORE', key, 0, current_time - window_size)
    
    -- 獲取當前請求數(shù)
    local count = redis.call('ZCARD', key)
    
    if count >= tonumber(ARGV[1]) then
        return 0
    end
    
    -- 添加當前請求記錄
    redis.call('ZADD', key, current_time, current_time..math.random())
    redis.call('EXPIRE', key, window_size)
    return 1
    
  2. 分布式ID生成器

    // Twitter的Snowflake算法變種實現(xiàn)
    public Long generateId(String bizType) {
        String key = "id_generator:" + bizType;
        long timestamp = System.currentTimeMillis();
        
        // 獲取序列號并自增
        long sequence = jedis.incr(key);
        jedis.expire(key, 3600);
        
        return ((timestamp - 1288834974657L) << 22) 
            | (datacenterId << 17)
            | (workerId << 12)
            | (sequence % 4096);
    }
    
  3. 精確計數(shù)與基數(shù)統(tǒng)計

    • 小數(shù)據(jù)量:直接使用INCR
    • 大數(shù)據(jù)量:結合HyperLogLog進行基數(shù)估算

2.3 分布式鎖的完整實現(xiàn)方案

分布式鎖的演進過程

  1. 基礎版本

    SET lock:resource unique_value NX EX 30
    
  2. 改進版本(解決鎖續(xù)期問題)

    // 加鎖
    String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
    
    // 啟動守護線程定期續(xù)期
    new Thread(() -> {
        while (locked) {
            jedis.expire(lockKey, expireTime/1000);
            Thread.sleep(expireTime/3);
        }
    }).start();
    
  3. Redlock算法實現(xiàn)

    // 多節(jié)點加鎖
    List<Jedis> jedisList = getRedisNodes();
    int successCount = 0;
    
    long startTime = System.currentTimeMillis();
    for (Jedis jedis : jedisList) {
        if (jedis.set(lockKey, value, "NX", "PX", expireTime) != null) {
            successCount++;
        }
    }
    
    // 檢查是否在大多數(shù)節(jié)點上加鎖成功
    boolean locked = successCount >= (jedisList.size()/2 + 1);
    

生產(chǎn)環(huán)境最佳實踐

  1. 鎖粒度控制

    • 細粒度鎖:按業(yè)務ID拆分(如order:123)
    • 粗粒度鎖:全局資源保護
  2. 異常處理

    try {
        if (acquireLock()) {
            // 業(yè)務邏輯
        }
    } finally {
        // 確保釋放鎖
        releaseLock();
    }
    
  3. 性能優(yōu)化

    • 避免長時間持有鎖
    • 使用tryLock模式(帶超時)
    • 鎖分段技術提升并發(fā)
  4. 鎖監(jiān)控

    # 監(jiān)控鎖狀態(tài)
    redis-cli --latency -h 127.0.0.1 -p 6379
    redis-cli slowlog get
    

集群環(huán)境特殊考量

  1. 主從切換問題

    • 使用Redlock算法
    • 監(jiān)控主從同步延遲
  2. 多數(shù)據(jù)中心部署

    • 跨機房延遲評估
    • 本地緩存與分布式鎖結合
  3. 鎖服務降級方案

    • 本地鎖降級
    • 樂觀鎖替代
    • 熔斷機制

三、多命令原子性實現(xiàn):事務與 Lua 腳本

當需要多個命令組合實現(xiàn)原子性時,Redis 提供了兩種方案:MULTI/EXEC事務和Lua 腳本。下面對比兩者的差異與適用場景。

3.1 MULTI/EXEC 事務:弱一致性的批量執(zhí)行

Redis 事務并非傳統(tǒng)數(shù)據(jù)庫的 ACID 事務,其核心特性是 "批量執(zhí)行 + 要么全部執(zhí)行,要么全部不執(zhí)行"(但不支持回滾)。

事務執(zhí)行流程詳解

  1. MULTI:標記事務開始,后續(xù)命令進入隊列
  2. 命令入隊:所有操作命令不會被立即執(zhí)行,而是返回"QUEUED"狀態(tài)
  3. EXEC:執(zhí)行所有隊列中的命令
  4. DISCARD:可選操作,用于取消事務
127.0.0.1:6379> MULTI  # 開啟事務
OK

127.0.0.1:6379> INCR counter:user  # 命令1:用戶數(shù)+1
QUEUED  # 命令入隊,未執(zhí)行

127.0.0.1:6379> SET user:1002:status "active"  # 命令2:設置用戶狀態(tài)
QUEUED

127.0.0.1:6379> EXEC  # 執(zhí)行事務,所有命令原子性執(zhí)行
1) (integer) 101  # INCR命令結果
2) OK  # SET命令結果

事務的局限性詳解

  1. 不支持回滾

    • 語法錯誤:事務中某個命令語法錯誤(如錯誤的命令名),整個事務都不會執(zhí)行
    • 運行時錯誤:如對字符串執(zhí)行INCR操作,錯誤命令會失敗,但其他命令仍會執(zhí)行
  2. 弱隔離性

    • 事務執(zhí)行期間會阻塞其他客戶端命令
    • 但事務內(nèi)的命令是"非原子性入隊"的(即入隊時不執(zhí)行,執(zhí)行時才獲取數(shù)據(jù))
    • 可能出現(xiàn)"WATCH"失效問題
  3. 無法處理并發(fā)沖突

    • 沒有類似數(shù)據(jù)庫的樂觀鎖機制
    • 兩個事務同時修改同一key時,后執(zhí)行的會覆蓋先執(zhí)行的結果

適用場景

  • 需要批量執(zhí)行多個命令,且不要求嚴格的事務隔離性
  • 簡單的計數(shù)器更新、狀態(tài)標記等場景
  • 配合WATCH實現(xiàn)簡單的樂觀鎖控制

3.2 Lua 腳本:強一致性的原子執(zhí)行

Redis 支持通過 Lua 腳本執(zhí)行自定義邏輯,且整個 Lua 腳本的執(zhí)行過程是原子性的—— 腳本執(zhí)行期間,Redis 不會中斷或執(zhí)行其他命令。這使得 Lua 腳本成為實現(xiàn)復雜原子邏輯的最佳選擇。

Lua 腳本的核心優(yōu)勢

  1. 完整的原子性

    • 腳本作為一個整體執(zhí)行,不會被其他命令打斷
    • 所有操作要么全部成功,要么全部失敗
  2. 豐富的邏輯控制

    • 支持條件判斷(if...else)
    • 支持循環(huán)(for/while)
    • 支持變量和復雜計算
  3. 網(wǎng)絡效率高

    • 多個命令打包成腳本,只需一次網(wǎng)絡往返
    • 特別適合高延遲環(huán)境

實踐案例:庫存扣減(避免超賣)

某商品庫存初始值為 100,需實現(xiàn) "用戶下單時原子性扣減庫存,庫存不足時返回失敗":

-- Lua腳本:KEYS[1]為庫存key,ARGV[1]為扣減數(shù)量
local stock = redis.call('get', KEYS[1])
if not stock or tonumber(stock) < tonumber(ARGV[1]) then
    return 0  # 庫存不足,扣減失敗
end
return redis.call('decrby', KEYS[1], ARGV[1])  # 原子性扣減庫存

Java調(diào)用示例:

String luaScript = "local stock = redis.call('get', KEYS[1])\n" +
                   "if not stock or tonumber(stock) < tonumber(ARGV[1]) then\n" +
                   "    return 0\n" +
                   "end\n" +
                   "return redis.call('decrby', KEYS[1], ARGV[1])";

List<String> keys = Collections.singletonList("stock:goods:1001");
List<String> args = Collections.singletonList("1");

// 執(zhí)行腳本
Long result = (Long) jedis.eval(luaScript, keys, args);

if (result == 0) {
    System.out.println("庫存不足");
} else {
    System.out.println("庫存扣減成功,剩余庫存:" + result);
}

腳本緩存優(yōu)化

Redis會緩存執(zhí)行過的腳本(通過SHA1校驗和),后續(xù)可通過evalsha調(diào)用:

# 首次執(zhí)行
127.0.0.1:6379> script load "return redis.call('get', KEYS[1])"
"a5a06e6a8a4b4a5a5a5a5a5a5a5a5a5a5a5a5a5"

# 后續(xù)執(zhí)行
127.0.0.1:6379> evalsha a5a06e6a8a4b4a5a5a5a5a5a5a5a5a5a5a5a5 1 mykey
"value"

適用場景

  • 需要嚴格原子性的復雜操作(如庫存扣減、秒殺)
  • 需要條件判斷的多步驟操作
  • 高頻操作需要減少網(wǎng)絡開銷的場景
  • 分布式鎖的實現(xiàn)(包含鎖的獲取、續(xù)期和釋放)

注意事項

  1. 腳本執(zhí)行時間不宜過長(默認5秒超時)
  2. 避免在腳本中執(zhí)行耗時操作
  3. 腳本應保持簡單,避免復雜計算

四、Redis 原子操作的常見問題與避坑指南

即使掌握了原子操作的用法,在實際開發(fā)中仍可能因細節(jié)處理不當導致問題。下面總結 4 個高頻坑點及解決方案,并提供具體優(yōu)化建議。

4.1 坑點 1:混淆 "單命令原子性" 與 "多命令原子性"

問題現(xiàn)象: 在電商秒殺場景中,開發(fā)者錯誤地認為多個獨立命令的組合具有原子性。例如以下庫存扣減邏輯:

# 錯誤示例:判斷庫存>0后扣減(非原子操作)
if redis.call('get', 'stock:1001') > 0 then
    redis.call('decr', 'stock:1001')  # 可能出現(xiàn)并發(fā)時庫存為負
end

問題原因

  • getdecr是兩個獨立命令,中間可能插入其他請求
  • 在高并發(fā)場景下,多個請求可能同時判斷庫存為正,導致"超賣"現(xiàn)象

解決方案

  1. 使用 Lua 腳本將"判斷+扣減"封裝為原子操作(完整示例見3.2節(jié))
  2. 或者直接使用DECR命令的返回值判斷(返回減后的值,若為負則不允許扣減)

4.2 坑點 2:分布式鎖未設置過期時間

典型場景: 在分布式任務調(diào)度系統(tǒng)中,使用Redis實現(xiàn)分布式鎖時出現(xiàn)以下問題:

# 錯誤加鎖方式(未設置過期時間)
SET lock:order_123 true NX

風險分析

  • 若客戶端崩潰或網(wǎng)絡異常,鎖將永遠無法釋放
  • 其他客戶端將無法獲取鎖,導致系統(tǒng)死鎖
  • 需要人工介入刪除key才能恢復

最佳實踐

  1. 必須使用帶過期時間的加鎖命令:
    SET lock:order_123 true NX EX 10
    
  2. 過期時間設置原則:
    • 大于業(yè)務執(zhí)行的最大耗時(如業(yè)務最多執(zhí)行5秒,設10秒)
    • 建議設置自動續(xù)期機制(如Redisson的watchdog)
  3. 配合唯一標識實現(xiàn)安全解鎖:
    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1])
    else
        return 0
    end
    

4.3 坑點 3:Lua 腳本執(zhí)行效率低下

性能問題案例: 某社交平臺在用戶Feed流處理腳本中,包含以下低效操作:

-- 低效腳本示例:遍歷所有粉絲進行計數(shù)
local followers = redis.call('SMEMBERS', 'user:'..userId..':followers')
local count = 0
for i, follower in ipairs(followers) do
    count = count + redis.call('SCARD', 'user:'..follower..':posts')
end
return count

影響分析

  • Redis單線程模型下,腳本執(zhí)行會阻塞其他命令
  • 當粉絲量達百萬級時,腳本執(zhí)行可能超過1秒
  • 導致Redis整體吞吐量下降,QPS驟降

優(yōu)化建議

  1. 腳本優(yōu)化原則:
    • 避免大數(shù)據(jù)集遍歷(改用SCAN分批處理)
    • 復雜計算移到客戶端(如排序、聚合)
    • 單個腳本執(zhí)行時間控制在10ms內(nèi)
  2. 改進方案:
    • 使用Redis的SCARD命令直接獲取集合基數(shù)
    • 或改用客戶端分批查詢后聚合

4.4 坑點 4:使用 INCR 實現(xiàn)分布式 ID 時的溢出問題

問題背景: 某物聯(lián)網(wǎng)平臺使用Redis生成設備ID:

INCR device:id_counter

潛在風險

  • Redis計數(shù)器最大值為2^63-1(約9e18)
  • 假設每天生成1億ID,約需2.5億年才會溢出
  • 但某些高頻場景(如日志ID)可能快速耗盡

解決方案

  1. 組合ID生成方案:
    # 時間戳(41bit) + 機器ID(10bit) + 序列號(12bit)
    INCR id:20230101  # 每日重置計數(shù)器
    
  2. 定期重置機制:
    EXPIRE id_counter 86400  # 每日自動過期
    
  3. 分片方案:
    INCR id_counter:{shard1}  # 按業(yè)務分片使用不同key
    

監(jiān)控建議

  • 對關鍵計數(shù)器設置監(jiān)控告警
  • 當計數(shù)值超過閾值時自動告警
  • 定期檢查計數(shù)器增長趨勢

到此這篇關于Redis 的原子性操作的文章就介紹到這了,更多相關Redis  原子性操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Redis實現(xiàn)集群搭建+集群讀寫的示例

    Redis實現(xiàn)集群搭建+集群讀寫的示例

    本文介紹了Redis集群的搭建和讀寫操作,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-02-02
  • Redis集群的相關詳解

    Redis集群的相關詳解

    這篇文章主要介紹了Redis集群的相關,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • Redis中的配置與優(yōu)化過程

    Redis中的配置與優(yōu)化過程

    本文系統(tǒng)介紹了Redis作為非關系型數(shù)據(jù)庫的特點,包括其高性能、多數(shù)據(jù)類型支持、內(nèi)存與持久化機制,對比了RDB與AOF的優(yōu)缺點及配置方式,最后講解了內(nèi)存優(yōu)化策略與性能管理方法
    2025-10-10
  • 利用yum安裝Redis的方法詳解

    利用yum安裝Redis的方法詳解

    Redis是一個開源的使用ANSI C語言編寫、支持網(wǎng)絡、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫,并提供多種語言的API。從2010年3月15日起,Redis的開發(fā)工作由VMware主持。這篇文章主要介紹的是利用yum安裝Redis的方法,有需要的朋友們可以參考借鑒,下面來一起看看吧
    2016-11-11
  • 淺談一下Redis的緩存穿透、擊穿和雪崩

    淺談一下Redis的緩存穿透、擊穿和雪崩

    這篇文章主要介紹了淺談一下Redis緩存穿透、擊穿和雪崩,緩存穿透是指在使用緩存系統(tǒng)時,頻繁查詢一個不存在于緩存中的數(shù)據(jù),導致這個查詢每次都要通過緩存層去查詢數(shù)據(jù)源,無法從緩存中獲得結果,需要的朋友可以參考下
    2023-08-08
  • Redisson之lock()和tryLock()的區(qū)別及說明

    Redisson之lock()和tryLock()的區(qū)別及說明

    這篇文章主要介紹了Redisson之lock()和tryLock()的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Redis性能大幅提升之Batch批量讀寫詳解

    Redis性能大幅提升之Batch批量讀寫詳解

    這篇文章主要給大家介紹了關于Redis性能大幅提升之Batch批量讀寫的相關資料,文中介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來跟著小編一起來學習學習吧。
    2017-06-06
  • Redis之sql緩存的具體使用

    Redis之sql緩存的具體使用

    本文主要介紹了Redis之sql緩存的具體使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Redis高效檢索地理位置的原理解析

    Redis高效檢索地理位置的原理解析

    這篇文章主要介紹了Redis是如何高效檢索地理位置,通過geo相關的命令,可以很容易在redis中存儲和使用經(jīng)緯度坐標信息,具體實現(xiàn)方法跟隨小編一起看看吧
    2021-06-06
  • Redis持久化方式之RDB和AOF的原理及優(yōu)缺點

    Redis持久化方式之RDB和AOF的原理及優(yōu)缺點

    在Redis中,數(shù)據(jù)可以分為兩類,即內(nèi)存數(shù)據(jù)和磁盤數(shù)據(jù),Redis?提供了兩種不同的持久化方式,其中?RDB?是快照備份機制,AOF?則是追加寫操作機制,本文將詳細給大家介紹Redis?持久化方式RDB和AOF的原理及優(yōu)缺點,感興趣的同學可以跟著小編一起來學習
    2023-06-06

最新評論

山西省| 长乐市| 雷山县| 河池市| 台州市| 揭阳市| 旅游| 阿拉善右旗| 昌吉市| 九江县| 长岛县| 汝城县| 玉门市| 九龙坡区| 嘉禾县| 葫芦岛市| 广安市| 凤庆县| 上高县| 敖汉旗| 南投县| 泸州市| 二连浩特市| 天门市| 龙江县| 汕尾市| 新源县| 黎平县| 花莲县| 罗平县| 康平县| 雷州市| 满城县| 莆田市| 枣庄市| 赫章县| 城固县| 武穴市| 达州市| 澄迈县| 石屏县|