zookeeper(3)分佈式鎖

在一個分佈式系統中,如何保證一個操作,同一時間只有一個線程可以執行,這就是分佈式鎖的使用場景,同一時間,只有一個線程可以獲得鎖的使用權。

如何實現一個分佈式鎖?

實現一個分佈式鎖,可以有以下3種方法。

一、基於數據庫實現分佈式鎖。

1、在MySQL中,使用悲觀鎖"select from t where id = for update"可以對行數據進行加鎖,來實現分佈式鎖。
2、同一個時間內,只會有一個線程加鎖成功,其他線程必須等待。
3、數據庫要保證是全局的,每一把鎖所對應的行數據也是唯一的。

優點:

1、實現簡單,方便。

缺點:

1、基於數據庫的悲觀鎖,性能比較差。
2、等待中的線程是自旋的或者等待狀態,需要等待持有鎖的線程處理完,多個線程再一起去競爭同一把鎖。
3、異常無法處理,當持有鎖的線程還沒有釋放鎖,意外退出,鎖資源將無法釋放,應用程序將無法繼續進行。

二、基於redis實現的分佈式鎖。

1、使用redis的setnx命令,可以模擬分佈式鎖,setnx保證操作一個key值,如果沒有則返回true,如果存在則返回false。
2、爲了解決程序意外退出導致無法釋放鎖資源,需要給key增加一個超時時間。
3、redis提供了帶參數的命令,可以保證設置key值和設置超時時間這2個操作的原子性

SET key value [EX seconds] [PX milliseconds] [NX|XX]

4、假如有如下情況:

A獲取了鎖,設置了超時時間爲10秒,但是A執行了15秒,在10秒的時候鎖失效了。
B在11秒的時候獲取了鎖,執行了5秒,那麼A在15秒的時候會誤刪掉B的鎖。

爲了解決以上問題,每個加鎖的線程都需要設置自己的value值,當刪除的時候也要校驗是自己的鎖,纔可以刪除。

優點:

1、實現簡單,方便。
2、基於redis的高性能,效率高。

缺點:

1、無法解決超時鎖失效問題,邏輯處理的時間超過了設置的超時時間,那麼這個時候就會導致,另一個也可以拿到鎖繼續執行。

爲了解決這個問題,一般的方法是啓動一個守護線程,時刻監控失效時間,當鎖時間超過一定的執行時間比例之後,自動續約一定的時間,當然這個總時間是有最大閾值限制的。

2、等待中的線程是自旋的後者等待狀態,需要等待持有鎖的線程處理完,多個線程再一起去競爭同一把鎖。

三、zookeeper實現分佈式鎖

方式1:悲觀鎖

1、使用臨時節點實現分佈式鎖,第一個創建臨時節點成功的線程,獲取鎖成功。
2、其他創建臨時節點的線程會失敗,那麼監聽鎖的臨時節點。
3、當鎖釋放的時候,刪除臨時節點,會通知到監聽的線程,收到通知的線程繼續嘗試創建臨時鎖節點,誰創建成功誰獲得鎖。

優點:

解決了鎖失效問題,通知機制可以完美解決,即使創建臨時節點的線程掛掉,臨時節點會自動刪除。

缺點:

1、當有大量線程,等待鎖資源的時候,鎖資源釋放會涉及到大量的通知,並且大量的線程需要一起競爭鎖資源。

方式2:樂觀鎖

1、在一個目錄下,各個線程創建順序的臨時節點,節點編號1、2、3、4、5等。
2、目錄下創建的節點最小的線程獲取鎖。
3、等待鎖資源的線程,不再一起全部監聽鎖節點,而是隻監聽比自己小的上一個節點。
4、當監聽的比自己小的鎖節點被刪除後,繼續改爲監聽上一個比自己小的節點。
4、當鎖釋放的時候,只需要通知監聽鎖節點的一個獲幾個線程,避免了大量的通知。

zookeeper實現的樂觀鎖,是比較合理的分佈式鎖方式,感興趣的朋友可以用代碼實現一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章