用redis構建分佈式鎖

 

用redis構建分佈式鎖

 

單實例的實現

從2.6.12版本開始,redis爲SET命令增長了一系列選項:html

  • EX seconds – 設置鍵key的過時時間,單位時秒
  • PX milliseconds – 設置鍵key的過時時間,單位時毫秒
  • NX – 只有鍵key不存在的時候纔會設置key的值
  • XX – 只有鍵key存在的時候纔會設置key的值

 

若是有2個進程(可能位於不一樣機器)須要競爭某個資源,能夠爲這個資源加鎖,鎖放在redis裏面,這樣兩個進程都能訪問到,例以下面的命令:git

SET resource-name random-value NX EX max-lock-time github

僅當key不存在時,設置一個鍵值對,而且設置了key的過時時間。redis

若是其中一個進程set成功,那麼另一個進程會set失敗,只要判斷set命令的返回值,就能夠判斷是否加鎖成功。算法

這裏resouce-name是須要加鎖的資源,而random-value每一個進程均可以寫惟一值,而max-lock-time是鎖的最大持有時間。安全

 

如何釋放鎖:服務器

a客戶端得到的鎖(鍵key)已經因爲過時時間到了被redis服務器刪除,可是這個時候a客戶端還去執行DEL命令。而b客戶端已經在a設置的過時時間以後從新獲取了這個一樣key的鎖,那麼a執行DEL就會釋放了b客戶端加好的鎖。網絡

if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del",KEYS[1])
else
    return 0
end

因爲每一個進程寫入的value是本身生成的隨機數,能夠保證一個進程只能刪除本身加的鎖,而避免誤刪其它進程加的鎖。dom

 

分佈式鎖

在分佈式版本的算法裏咱們假設咱們有N個Redis master節點,這些節點都是徹底獨立的,咱們不用任何複製或者其餘隱含的分佈式協調算法。咱們已經描述瞭如何在單節點環境下安全地獲取和釋放鎖。所以咱們理所固然地應當用這個方法在每一個單節點裏來獲取和釋放鎖。在咱們的例子裏面咱們把N設成5,這個數字是一個相對比較合理的數值,所以咱們須要在不一樣的計算機或者虛擬機上運行5個master節點來保證他們大多數狀況下都不會同時宕機。一個客戶端須要作以下操做來獲取鎖:分佈式

1.獲取當前時間(單位是毫秒)。

2.輪流用相同的key和隨機值在N個節點上請求鎖,在這一步裏,客戶端在每一個master上請求鎖時,會有一個和總的鎖釋放時間相比小的多的超時時間。好比若是鎖自動釋放時間是10s,那每一個節點鎖請求的超時時間多是5~50ms的範圍,這個能夠防止一個客戶端在某個宕掉的master節點上阻塞過長時間,若是一個master節點不可用了,咱們應該儘快嘗試下一個master節點。

3.客戶端計算第二步中獲取鎖所花的時間,只有當客戶端在大多數master節點上成功獲取了鎖(在這裏是3個),並且總共消耗的時間不超過鎖釋放時間,這個鎖就認爲是獲取成功了。

4.若是鎖獲取成功了,那如今鎖自動釋放時間就是最初的鎖釋放時間減去以前獲取鎖所消耗的時間。

5.若是鎖獲取失敗了,不論是由於獲取成功的鎖不超過一半(N/2+1)仍是由於總消耗時間超過了鎖釋放時間,必定要儘快在獲取鎖成功的節點上釋放鎖,這樣就不必等到key超時後才能從新獲取這個鎖(可是若是網絡分區的狀況發生並且客戶端沒法鏈接到Redis節點時,會損失等待key超時這段時間的系統可用性)。

 

注意:當一個客戶端獲取鎖失敗時,這個客戶端應該在一個隨機延時後進行重試,之因此採用隨機延時是爲了不不一樣客戶端同時重試致使誰都沒法拿到鎖的狀況出現。一樣的道理客戶端越快嘗試在大多數Redis節點獲取鎖,出現多個客戶端同時競爭鎖和重試的時間窗口越小,可能性就越低,因此最完美的狀況下,客戶端應該用多路傳輸的方式同時向全部Redis節點發送SET命令。

 

 

 

 

參考文檔:

http://ifeve.com/redis-lock/

https://github.com/SPSCommerce/redlock-py

相關文章
相關標籤/搜索