在一個分佈式系統中,如何保證一個操做,同一時間只有一個線程能夠執行,這就是分佈式鎖的使用場景,同一時間,只有一個線程能夠得到鎖的使用權。redis
實現一個分佈式鎖,能夠有如下3種方法。數據庫
一、在MySQL中,使用悲觀鎖"select from t where id = for update"能夠對行數據進行加鎖,來實現分佈式鎖。
二、同一個時間內,只會有一個線程加鎖成功,其餘線程必須等待。
三、數據庫要保證是全局的,每一把鎖所對應的行數據也是惟一的。分佈式
一、實現簡單,方便。ide
一、基於數據庫的悲觀鎖,性能比較差。
二、等待中的線程是自旋的或者等待狀態,須要等待持有鎖的線程處理完,多個線程再一塊兒去競爭同一把鎖。
三、異常沒法處理,當持有鎖的線程尚未釋放鎖,意外退出,鎖資源將沒法釋放,應用程序將沒法繼續進行。性能
一、使用redis的setnx命令,能夠模擬分佈式鎖,setnx保證操做一個key值,若是沒有則返回true,若是存在則返回false。
二、爲了解決程序意外退出致使沒法釋放鎖資源,須要給key增長一個超時時間。
三、redis提供了帶參數的命令,能夠保證設置key值和設置超時時間這2個操做的原子性線程
SET key value [EX seconds] [PX milliseconds] [NX|XX]
四、假若有以下狀況:code
A獲取了鎖,設置了超時時間爲10秒,可是A執行了15秒,在10秒的時候鎖失效了。
B在11秒的時候獲取了鎖,執行了5秒,那麼A在15秒的時候會誤刪掉B的鎖。資源
爲了解決以上問題,每一個加鎖的線程都須要設置本身的value值,當刪除的時候也要校驗是本身的鎖,才能夠刪除。it
一、實現簡單,方便。
二、基於redis的高性能,效率高。class
一、沒法解決超時鎖失效問題,邏輯處理的時間超過了設置的超時時間,那麼這個時候就會致使,另外一個也能夠拿到鎖繼續執行。
爲了解決這個問題,通常的方法是啓動一個守護線程,時刻監控失效時間,當鎖時間超過必定的執行時間比例以後,自動續約必定的時間,固然這個總時間是有最大閾值限制的。
二、等待中的線程是自旋的後者等待狀態,須要等待持有鎖的線程處理完,多個線程再一塊兒去競爭同一把鎖。
一、使用臨時節點實現分佈式鎖,第一個建立臨時節點成功的線程,獲取鎖成功。
二、其餘建立臨時節點的線程會失敗,那麼監聽鎖的臨時節點。
三、當鎖釋放的時候,刪除臨時節點,會通知到監聽的線程,收到通知的線程繼續嘗試建立臨時鎖節點,誰建立成功誰得到鎖。
解決了鎖失效問題,通知機制能夠完美解決,即便建立臨時節點的線程掛掉,臨時節點會自動刪除。
一、當有大量線程,等待鎖資源的時候,鎖資源釋放會涉及到大量的通知,而且大量的線程須要一塊兒競爭鎖資源。
一、在一個目錄下,各個線程建立順序的臨時節點,節點編號一、二、三、四、5等。
二、目錄下建立的節點最小的線程獲取鎖。
三、等待鎖資源的線程,再也不一塊兒所有監聽鎖節點,而是隻監聽比本身小的上一個節點。
四、當監聽的比本身小的鎖節點被刪除後,繼續改成監聽上一個比本身小的節點。
四、當鎖釋放的時候,只須要通知監聽鎖節點的一個獲幾個線程,避免了大量的通知。
zookeeper實現的樂觀鎖,是比較合理的分佈式鎖方式,感興趣的朋友能夠用代碼實現一下。