目錄
Zookeeper是如何實現分布式鎖的
標簽 : Zookeeper 分布式
實現分布式鎖要考慮的重要問題
1. 三個核心要素
加鎖, 解鎖, 鎖超時
2. 三個問題
- 要保證原子性操作, 加鎖和鎖超時的操作要一次性執行完畢
- 防止誤刪鎖
- 在誤刪的基礎上, 加一個守護線程, 為鎖續命.
什么是臨時順序節點
Zookeeper的數據存儲結構就像是一棵樹, 這棵樹由節點組成, 這種節點叫做Znode. Znode分為四種類型.
1. 持久節點(Persistent)
默認的節點類型, 創建節點的客戶端和Zookeeper斷開連接之后, 該節點依舊存在.
2. 持久順序節點(Persistent Sequential)
所謂順序節點, 就是在創建節點的時候, Zookeeper根據節點的創建時間順序給節點的名稱進行編號.

3. 臨時節點(Ephemeral)
和持久節點相反, 當創建節點的客戶端與Zookeeper斷開連接之后,臨時節點會被刪除.



4. 臨時順序節點(Ephemeral Sequential)
在創建節點時, Zookeeper根據創建的時間順序給該節點名稱進行編號; 當創建節點的客戶端與Zookeeper斷開連接之后,臨時節點會被刪除.
Zookeeper實現分布式鎖的原理
上面已經說了Znode的四種類型, 其中最后一種類型 臨時順序節點 是最有利於實現Zookeeper分布式鎖的.
1. 獲取鎖
- 首先, 在Zookeeper當中創建一個持久節點ParentLock. 當第一個客戶端想要獲得操作某項數據的鎖的時候,需要在該持久節點之下簡歷一個臨時順序節點
Lock1.

- 之后,
客戶端1查找ParentLock下面所有的臨時順序節點並按照大小排序, 判斷自己所創建的節點Lock1是不是順序最靠前的一個. 如果是第一個節點,則成功獲得鎖.

- 此時,
客戶端2前來獲取該項數據的鎖, 則在ParentLock下再創建一個臨時順序節點Lock2.

客戶端2查找ParentLock下面所有的臨時順序節點並排序,發現自己的Lock2節點並不是最靠前的. 於是客戶端2向排序僅僅比它靠前的Lock1注冊Watcher, 用於監聽Lock1動態. 這意味着Lock2搶鎖失敗, 進入等待狀態.

- 此時, 如果有一個
客戶端3前來獲取鎖, 則在ParentLock下在創建一個臨時順序節點Lock3.

客戶端3查找ParentLock下面所有的臨時順序節點並排序, 判斷自己所創建的節點Lock3是不是順序最靠前的一個, 結果發現Lock3不是最靠前的. 於是客戶端3同樣搶鎖失敗, 進入了等待狀態.

- 這個時候
客戶端1得到了鎖,客戶端2監聽了客戶端1,客戶端3監聽了客戶端2. 這樣剛好形成一個等待的隊列.
2. 釋放鎖
釋放鎖有兩種情況
2.1 任務完成, 客戶端顯示釋放
- 當任務完成時,
客戶端1會顯示的調用刪除節點Lock1的指令.

2.2 任務執行過程中, 客戶端崩潰
- 獲得鎖的
客戶端1在執行任務的過程中, 如果崩潰, 則會斷開和Zookeeper服務器的鏈接, 根據臨時節點的特性, 相關聯的Lock1會隨之自動刪除.

3. 獲得鎖
- 由於
客戶端2一直在監聽Lock1的狀態,這個時候發現Lock1注銷了,客戶端2會立即接收到通知. 這個時候客戶端2會再次查詢ParentLock下的所有節點, 確認自己所創建的節點是不是最小的節點, 如果是最小的則成功獲得鎖.

同理可推至
客戶端3
總結
Zookeeper和Redis分布式鎖的比較
| 分布式鎖 | 優點 | 缺點 |
|---|---|---|
| Zookeeper | 1.有封裝好的框架,容易實現. 2.有等待鎖的機制( Watcher),可以提高搶鎖的效率,好處多多 |
添加和刪除節點的性能比較低 |
| Redis | Set和Del的性能比較高(畢竟鍵值數據庫,Hash) |
1.實現復雜,需要考慮原子性,誤刪等情況. 2.沒有等待鎖的機制,只能通過客戶端的自旋來等鎖,效率低下. |
9: https://www.funtl.com/assets/Lusifer2018101900098]: https://www.funtl.com/assets/Lusifer201810190008.png
