這篇文章主要是對 Redis
官方網站刊登的 Distributed locks with Redis 部份內容的總結和翻譯。程序員
Redis
官方站這篇文章提出了一種權威的基於 Redis
實現分佈式鎖的方式名叫 Redlock,此種方式比原先的單節點的方法更安全。它能夠保證如下特性:redis
client
能拿到鎖client
均可能拿到鎖,不會出現死鎖的狀況,即便本來鎖住某資源的 client crash
了或者出現了網絡分區Redis
節點存活就能夠正常提供服務SET resource_name my_random_value NX PX 30000算法
主要依靠上述命令,該命令僅當 Key
不存在時(NX
保證)set
值,而且設置過時時間 3000ms
(PX
保證),值 my_random_value
必須是全部 client
和全部鎖請求發生期間惟一的,釋放鎖的邏輯是:安全
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
複製代碼
上述實現能夠避免釋放另外一個 client
建立的鎖,若是隻有 del
命令的話,那麼若是 client1
拿到 lock1
以後由於某些操做阻塞了很長時間,此時 Redis
端 lock1
已通過期了而且已經被從新分配給了 client2
,那麼 client1
此時再去釋放這把鎖就會形成 client2
本來獲取到的鎖被 client1
無端釋放了,但如今爲每一個 client
分配一個 unique
的 string
值能夠避免這個問題。至於如何去生成這個 unique string
,方法不少隨意選擇一種就好了。網絡
算法很易懂,起 5
個 master
節點,分佈在不一樣的機房儘可能保證可用性。爲了得到鎖,client
會進行以下操做:dom
ms
)單位5
個實例上申請鎖,固然須要使用相同的 key
和 random value
,這裏一個 client
須要合理設置與 master
節點溝通的 timeout
大小,避免長時間和一個 fail
了的節點浪費時間client
在大於等於 3
個 master
上成功申請到鎖的時候,且它會計算申請鎖消耗了多少時間,這部分消耗的時間採用得到鎖的當下時間減去第一步得到的時間戳獲得,若是鎖的持續時長(lock validity time
)比流逝的時間多的話,那麼鎖就真正獲取到了。lock validity time
應該是 origin(lock validity time) - 申請鎖期間流逝的時間client
申請鎖失敗了,那麼它就會在少部分申請成功鎖的 master
節點上執行釋放鎖的操做,重置狀態若是一個 client
申請鎖失敗了,那麼它須要稍等一會在重試避免多個 client
同時申請鎖的狀況,最好的狀況是一個 client
須要幾乎同時向 5
個 master
發起鎖申請。另外就是若是 client
申請鎖失敗了它須要儘快在它曾經申請到鎖的 master
上執行 unlock
操做,便於其餘 client
得到這把鎖,避免這些鎖過時形成的時間浪費,固然若是這時候網絡分區使得 client
沒法聯繫上這些 master
,那麼這種浪費就是不得不付出的代價了。分佈式
放鎖操做很簡單,就是依次釋放全部節點上的鎖就好了性能
若是咱們的節點沒有持久化機制,client
從 5
個 master
中的 3
個處得到了鎖,而後其中一個重啓了,這時注意:網站
整個環境中又出現了 3 個 master 可供另外一個 client 申請同一把鎖!lua
這違反了互斥性。
若是咱們開啓了 AOF
持久化那麼狀況會稍微好轉一些,由於 Redis
的過時機制是語義層面實現的,因此在 server
掛了的時候時間依舊在流逝,重啓以後鎖狀態不會受到污染。可是考慮斷電以後呢,AOF
部分命令沒來得及刷回磁盤直接丟失了,除非咱們配置刷回策略爲 fsnyc = always
,但這會損傷性能。解決這個問題的方法是,當一個節點重啓以後,咱們規定在max TTL
期間它是不可用的,這樣它就不會干擾本來已經申請到的鎖,等到它 crash
前的那部分鎖都過時了,環境不存在歷史鎖了,那麼再把這個節點加進來正常工做。
這是一個不定時更新的、披着程序員外衣的文青小號。既分享極客技術,也記錄人間煙火。