REDIS 學習(10)流程圖解使用redis實現分散式鎖
redis作為集中式快取,可以通過它來實現分散式鎖。
首先用到的redis操作有:
setnx key value: 當key不存在的時候生效並返回1,當已經有此key的時候返回0
getset key value: 設定新值返回舊值,如果之前不存在也設定新值並返回nil
get key: 返回對應的值,沒有則返回nil
del key,key1,key2: 刪除,返回刪除成功的個數
方案1,使用setnx key value來獲得鎖,del來釋放鎖
圖1
方案1有個問題是,如果獲得鎖的執行緒或者程序崩潰了,這個鎖將得不到釋放
方案2我們增加將值設定為當前時間,引入超時判斷
圖2
方案2依然有個問題,併發情況下,同時判斷鎖超時,n0,n1,n2同時del並通過setnx獲取鎖可能,會n2把n1獲取的鎖給刪除掉。
方案3增加getset 獲取舊的時間設定新的時間,這樣刪除某個超時鎖的操作只有一起,其他的獲取鎖判斷會接下來判斷鎖沒有超時。
圖3
注意虛線部分,這是之前其他某個博主的做法,就是誰設定了有效時間戳後就認為獲取了這個鎖。而實線部分我的做法是獲取超時鎖後就釋放掉,然後重新獲取鎖。兩者應該區別不是太大,都是通過getset解決超時鎖只進行一次刪除的問題。
最後,依然會有個懸而未決的問題,最初獲取這個鎖的執行緒或程序,如果在超時後,鎖被其他執行緒或者程序重新獲取後,這個執行緒或者程序如果復活了,也會再次觸發釋放鎖(del)?
所以我認為,del操作應該增加類似版本號的判斷,本文例子用它的時間戳即可
2017年2月7日更正:
由於setnx不能設定過期時間,所以通過setnx結合expire的方式在圖1中就能實現鎖的過期釋放策略:
if(setnx(a,'tag') =ok){
expire a 1;
}
不過set a tag ex 1 nx就已經實現了setnx+expire這兩步操作,設值返回ok,已經存在返回null。由於是一個redis命令所以是原子操作。而且未來的redis版本可能去掉setnx和setex, 因此推薦用set ex nx的方式。當然為了防止假死的執行緒復活後刪除鎖,在set 的時候依然可以通過增加本版本號或者使用隨機數的方式來處理。