分佈式鎖實現方法

分佈式鎖

目的

分佈式鎖的目的是達到數據的最終一致性;或者在分佈式部署集羣中,我們需要保證一個方法在同一時間內只能被一臺機器上的一個線程執行

要求

1、這把鎖要是一把可重入鎖(避免死鎖)
2、這把鎖最好是一把阻塞鎖
3、有高可用的獲取鎖和釋放鎖功能
4、獲取鎖和釋放鎖的性能要好

理論

分佈式的CAP理論告訴我們“任何一個分佈式系統都無法同時滿足一致性(Consistency)可用性(Availability)分區容錯性(Partition tolerance),最多隻能同時滿足兩項。所以,很多系統在設計之初就要對這三者做出取捨。在互聯網領域的絕大多數的場景中,都需要犧牲強一致性來換取系統的高可用性,系統往往只需要保證最終一致性,只要這個最終時間是在用戶可以接受的範圍內即可。

解決方案

  • 基於數據庫實現分佈式鎖
  • 基於緩存(redis,memcached,tair)實現分佈式鎖

基於數據庫實現分佈式鎖

1、創建一張鎖表
在這裏插入圖片描述
2、當我們要鎖住某個方法或資源時,我們就在該表中增加一條記錄,因爲我們對method_name做了唯一性約束,這裏如果有多個請求同時提交到數據庫的話,數據庫會保證只有一個操作可以成功,那麼我們就可以認爲操作成功的那個線程獲得了該方法的鎖,可以執行方法體內容。

3、當我們想要釋放鎖的時候就刪除這條記錄。

問題及解決方案

1、這把鎖強依賴數據庫的可用性,數據庫是一個單點,一旦數據庫掛掉,會導致業務系統不可用。

數據庫表搞成主備同步

2、這把鎖沒有失效時間,一旦解鎖操作失敗,就會導致鎖記錄一直在數據庫中,其他線程無法再獲得到鎖。

新增定時任務,定時就數據庫的超時數據刪除一遍

3、這把鎖只能是非阻塞的,因爲數據的insert操作,一旦插入失敗就會直接報錯。沒有獲得鎖的線程並不會進入排隊隊列,要想再次獲得鎖就要再次觸發獲得鎖操作,性能較差。

// 每次重試直到成功
while(true) {
    retry insert until success
}

4、這把鎖是非重入的,同一個線程在沒有釋放鎖之前無法再次獲得該鎖。因爲數據中數據已經存在了。

在數據庫表中新增字段,記錄當前獲得鎖的機器的主機信息和線程信息,那麼下次再獲取鎖的時候先查詢數據庫,如果當前機器的主機信息和線程信息在數據庫可以查到的話,直接把鎖分配給他就可以了。

基於緩存_tair實現分佈式鎖

在這裏插入圖片描述
tair在put時,如果傳入的 key 不存在,且傳入指定的version,則tair在保存數據時,會將緩存的version設定爲version+1;當 key 存在時,會判斷version是不是和tair中的一致,一致才允許修改;如圖中所示,第一次put時,傳入 version 爲2,則實際保存的version=3;下次再put時,如果 key 已存在,version則肯定和tair中的不一致;

所以當 put 成功時,則獲取到鎖;其他線程無法再獲取

問題及解決方案

1、tair 可以集羣部署,沒有單點的問題
2、解鎖失敗問題:tair的put方法支持傳入失效時間,到達時間之後數據會自動刪除。但是設定多長是新的問題,過短可能當前線程還沒執行完,過長可能其他線程等待時間加長
3、鎖是非阻塞的,同樣可通過 while true 循環獲取鎖,直到成功

// 每次重試直到成功
while(true) {
    retry tair.put until success
}

4、這把鎖是非重入的,同一個線程在沒有釋放鎖之前無法再次獲得該鎖。因爲緩存中數據已經存在了。

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