Redis_如何保證原子操作

 
需求:兩個客戶端同時對[key1]執行自增操作,不會相互影響
 
操作:下面兩個客戶端併發操作會導致[key1]輸出結果與預期不一致
  1. [客戶端一]讀取[key1],值爲[1]
  2. [客戶端二]讀取[key1],值爲[1]
  3. [客戶端一]將[key1]自增1,值爲[2]
  4. [客戶端二]將[key1]自增1,值爲[2]
  5. [客戶端一]輸出[key1],值爲[2]
  6. [客戶端二]輸出[key2],值爲[2]
 
解決思路
  1. [客戶端一]、[客戶端二]的R(讀)、M(自增)、W(寫)三個操作作爲一個原子操作執行
  2. [客戶端]對RMW整個操作過程加鎖,加鎖期間其它客戶端不能對[key1]執行寫操作
  3. Lua腳本
 
思路一:單命令操作
1. 概念
Redis 提供了 INCR/DECR/SETNX 命令,把RMW三個操作轉變爲一個原子操作 Redis 是使用單線程串行處理客戶端的請求來操作命令,所以當 Redis 執行某個命令操作時,其他命令是無法執行的,這相當於命令操作是互斥執行的
 
思路二:加鎖
1. 概念
加鎖主要是將多客戶端線程調用相同業務方法轉換爲串行化處理,比如多個客戶端調用同一個方法對某個鍵自增(這裏不考慮其它方法或業務會對該鍵同時執行自增操作)
 
調用SETNX命令對某個鍵進行加鎖(如果獲取鎖則執行後續RMW操作,否則直接返回未獲取鎖提示) 執行RMW業務操作 調用DEL命令刪除鎖

  

2. 加鎖風險一
假如某個客戶端在執行了SETNX命令加鎖之後,在後面操作業務邏輯時發生了異常,沒有執行 DEL 命令釋放鎖。 
該鎖就會一直被這個客戶端持有,其它客戶端無法拿到鎖,導致其它客戶端無法執行後續操作。 

解決思路:給鎖變量設置一個過期時間,到期自動釋放鎖
SET key value [EX seconds
| PX milliseconds] [NX]

 

3. 加鎖風險二
如果客戶端 A 執行了 SETNX 命令加鎖後,客戶端 B 執行 DEL 命令釋放鎖,此時,客戶端 A 的鎖就被誤釋放了。如果客戶端 C 正好也在申請加鎖,則可以成功獲得鎖。 

解決思路:加鎖操作時給每個客戶端設置一個唯一值(比如UUID),唯一值可以用來標識當前操作的客戶端。在釋放鎖操作時,客戶端判斷當前鎖變量的值是否和唯一標識相等,只有在相等的情況下,才能釋放鎖。(同一客戶端線程中加鎖、釋放鎖) 

SET lock_key unique_value NX PX 10000

 

思路三:Lua腳本
 
1. 概念
多個操作寫到一個 Lua 腳本中(Redis 會把整個 Lua 腳本作爲一個整體執行,在執行的過程中不會被其他命令打斷,從而保證了 Lua 腳本中操作的原子性)
 
2. 需求
限制所有客戶端在一定時間範圍內對某個方法(鍵)的訪問次數。客戶端 IP 作爲 key,某個方法(鍵)的訪問次數作爲 value
 
3. 腳本
local current current = redis.call("incr",KEYS[1]) 

if tonumber(current) == 1
then redis.call("expire",KEYS[1],60)
end

 

4. 調用執行
redis-cli --eval lua.script keys , args

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章