Redis 並發競爭key問題如何解決?
- 2019 年 11 月 10 日
- 筆記
1. 問題描述
並發競爭key這個問題簡單講就是:
同時有多個客戶端去set一個key。
示例場景 1
例如有多個請求一起去對某個商品減庫存,通常操作流程是:
- 取出當前庫存值
- 計算新庫存值
- 寫入新庫存值
假設當前庫存值為 20
,現在有2個連接都要減 5
,結果庫存值應該是 10
才對,但存在下面這種情況:

示例場景 2
比如有3個請求有序的修改某個key,按正常順序的話,數據版本應該是 1->2->3
,最後應該是 3
。
但如果第二個請求由於網絡原因遲到了,數據版本就變為了 1->3->2
,最後值為 2
,出問題了。
2. 解決方案
2.1 樂觀鎖
樂觀鎖適用於大家一起搶着改同一個key,對修改順序沒有要求的場景。
watch
命令可以方便的實現樂觀鎖。
需要注意的是,如果你的 redis 使用了數據分片的方式,那麼這個方法就不適用了。
watch
命令會監視給定的每一個key,當 exec
時如果監視的任一個key自從調用watch後發生過變化,則整個事務會回滾,不執行任何動作。

2.2 分佈式鎖
適合分佈式環境,不用關心 redis 是否為分片集群模式。
在業務層進行控制,操作 redis 之前,先去申請一個分佈式鎖,拿到鎖的才能操作。
分佈式鎖的實現方式很多,比如 ZooKeeper、Redis 等。
2.3 時間戳
適合有序需求場景,例如 A
需要把 key 設置為 a
,然後 B
設置為 b
,C
再設置為 c
,最後的值應該是 c
。
這時就可以考慮使用時間戳的方式:
A => set key1 {a 11:01} B => set key1 {b 11:02} C => set key1 {c 11:03}
就是在寫入時保存一個時間戳,寫入前先比較自己的時間戳是不是早於現有記錄的時間戳,如果早於,就不寫入了。
假設 B 先執行了,key1 的值為 {b 11:02}
,當A執行時,發現自己的時間戳11:01
早於現有值,就不執行 set 操作了。
2.4 消息隊列
在並發量很大的情況下,可以通過消息隊列進行串行化處理。這在高並發場景中是一種很常見的解決方案。
3. 小結
「Redis 並發競爭」 問題就是高並發寫同一個key時導致的值錯誤。
常用的解決方法:
- 樂觀鎖,注意不要在分片集群中使用
- 分佈式鎖,適合分佈式系統環境
- 時間戳,適合有序場景
- 消息隊列,串行化處理