1. 程式人生 > >REDIS 學習(10)流程圖解使用redis實現分散式鎖

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 的時候依然可以通過增加本版本號或者使用隨機數的方式來處理。