建立一張鎖表,當要鎖住某個資源時,就在該表中增長一條記錄,想要釋放鎖的時候就刪除這條記錄。html
Zookeeper 的分佈式鎖是經過臨時節點(EPHEMERAL)實現的。當客戶端會話終止或超時後 Zookeeper 會自動刪除臨時節點。 加鎖流程: 假設鎖空間的根節點爲 /_locknode_node
zookeeper若是長時間沒有檢測到客戶端心跳的時候就會認爲Session過時,那麼這個Session所建立的全部的臨時節點都會被刪除。
因此網絡、jvm gc等緣由致使長時間沒有收到心跳,會致使多客戶端操做共享資源。redis
zookeeper在集羣模式下,Client發的請求只會由Leader執行,發給Follwer的請求會轉發給Leader。Leader接收到請求後會通知Follwer進行投票,Follwer把投票結果發送給Leader,只要半數以上返回了ACK信息就認爲經過,則執行 commit ,同時提交本身。再返回給Client。
假設Follwer宕機,是不會轉發到Leader,因此不會獲取到鎖。假設Leader宕機,就不會進行消息廣播,會先進行選取新Leader再處理,因此也不會丟失信息。算法
Redis文檔中描述了單實例實現分佈式鎖的正確方法:sql
//添加鎖
SET resource_name my_random_value NX PX 30000
複製代碼
//釋放鎖
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
複製代碼
添加鎖時,key 的值是 my_random_value (一個隨機值),釋放鎖時先經過判斷當前 key 的值是否等於指定的值,這樣能夠避免誤刪,而且經過lua腳本實現原子性操做。
若是不是原子性操做也會致使誤刪。del 命令若是因網絡等緣由延時,這時恰好 key 過時被另外一個客戶端獲取了共享資源,而後 del 命令又到達 redis 實例致使誤刪。數據庫
單實例有兩個缺點,1. 如何設定過時時間,2. 沒有高可用。apache
假設有一 Master 一 Slave ,當 Master 節點獲取到鎖以後,還沒同步到 Slave ,Master 就宕機發生主備切換,Slave 晉升爲 Master ,此時再申請鎖時,就會獲取到同一共享資源。服務器
對於這種狀況,redis的做者antirez提出了RedLock算法(但依然沒有解決設定過時時間的問題),獲取大多數節點的鎖就算加鎖成功,流程以下(來自官方文檔):
假設有N個Redis master(文檔假設有5個,大多數就是大於等於3),這些節點徹底互相獨立,不存在主從複製或者其餘集羣協調機制網絡
崩潰恢復 假設有5個節點,如今客戶端成功向3個節點加鎖,由於大於等於N/2+1個Redis實例,因此加鎖成功。這是3個節點中某一個節點宕機重啓,則其餘客戶端有機會向重啓節點和另外兩個節點加鎖。致使共享資源被屢次操做。
對於該狀況,做者提出了延遲重啓的方案,即當一個Redis 節點重啓後,只要它不參與到任意當前活動的鎖,爲了達到這種效果,咱們只須要將新的 Redis 實例,在一個TTL時間內,對客戶端不可用便可,在這個時間內,全部客戶端鎖將被失效或者自動釋放。dom
[1] zookeeper.apache.org/doc/current…
[2] Distributed locks with Redis