分散式鎖的區別

好名字啊。發表於2021-11-30
分散式鎖,是一種思想,它的實現方式有很多。比如,我們將沙灘當做分散式鎖的元件,那麼它看起來應該是這樣的
  • 加鎖

在沙灘上踩一腳,留下自己的腳印,就對應了加鎖操作。其他程式或者執行緒,看到沙灘上已經有腳印,證明鎖已被別人持有,則等待。

  • 解鎖

把腳印從沙灘上抹去,就是解鎖的過程。

  • 鎖超時

為了避免死鎖,我們可以設定一陣風,在單位時間後颳起,將腳印自動抹去。

分散式鎖的實現有很多,比如基於資料庫、memcached、Redis、系統檔案、zookeeper等。它們的核心的理念跟上面的過程大致相同。具備的條件:

1、一個方法同一時間只能被一個機器一個執行緒執行

2、高可用的獲取鎖和釋放鎖

3、高效能的獲取鎖和釋放鎖

4、具備可重入性

5、具備鎖失效機制,防止死鎖

6、具備非阻塞鎖特性,即沒有獲取鎖直接放回獲取鎖失敗。

那麼如何實現分散式鎖呢,有如下幾種方式:

  1. 基於Zookeeper實現
  2. 基於快取(redis實現)
  3. 基於資料庫實現方式

一、基於Zookeeper實現

Zookeeper資料儲存結構是一顆樹,樹由節點組成,節點叫ZNode

Znode分四種型別:

1、持久節點(persistent)

預設節點型別,建立節點的客戶端和Zookeeper斷開連結後,節點依舊存在

2、持久節點順序節點(persistent_sequential)

建立節點時,根據建立時間給節點編號。

3、臨時節點

斷開連結後,節點被刪除

4、臨時順序節點

Zookeeper分散式鎖的原理:

獲取鎖:

  1. 在Zookeeper建立一個持久節點ParentLock,當客戶端想要獲取鎖時,在ParentLock節點下建立臨時順序節點。
  2. 然後客戶端再去獲取臨時節點是否是最靠前的一個,如果是則獲取鎖。
  3. 另外一個客戶端先建立臨時節點,然後獲取臨時節點是靠前,如果不是靠前的,並且不是最小的序號,此時向前面的節點註冊Watcher,用於監聽前一個節點的鎖是否存在。

釋放鎖:

  1. 任務完成刪除臨時節點
  2. 由於節點都是相互監聽,當前一個節點消失,下一個節點被置頂
  3. 如果機器當機了,會自動刪除臨時節點。

缺點:

  1. 效能上不如快取服務高,建立鎖和釋放鎖過程上,都動態建立、銷燬臨時節點實現鎖功能,Zk中建立和刪除節點只能通過leader伺服器執行,然後將資料同步到所有follower機器上。

  2. 可能會因為網路抖動導致連結中斷,刪除臨時節點,但是ZK有多種重試策略,重試之後才會刪除臨時節點。

二、基於快取(redis實現)

1、加鎖

加鎖實際上就是在redis中,給Key鍵設定一個值,為避免死鎖,並給定一個過期時間。

SET lock_key random_value NX PX 5000

值得注意的是:

random_value 是客戶端生成的唯一的字串。
NX 代表只在鍵不存在時,才對鍵進行設定操作。
PX 5000 設定鍵的過期時間為5000毫秒。
這樣,如果上面的命令執行成功,則證明客戶端獲取到了鎖。

2、解鎖

解鎖的過程就是將Key鍵刪除。但也不能亂刪,不能說客戶端1的請求將客戶端2的鎖給刪除掉。這時候random_value的作用就體現出來。

為了保證解鎖操作的原子性,我們用LUA指令碼完成這一操作。先判斷當前鎖的字串是否與傳入的值相等,是的話就刪除Key,解鎖成功。

 if redis.call('get',KEYS[1]) == ARGV[1] then
	 return redis.call('del',KEYS[1])
 else
 	return 0
 end

3、使用

首先,我們在pom檔案中,引入Redis包。在這裡,筆者用的是最新版本,注意由於版本的不同,API可能有所差異。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

加鎖的過程很簡單,就是通過SET指令來設定值,成功則返回;否則就迴圈等待,在timeout時間內仍未獲取到鎖,則獲取失敗。

缺點:

客戶端A從master獲取鎖,master將鎖同步到slave錢,master 當機,slave節點晉升master節點,客戶端B取得了被客戶端A鎖定的同一個資源。安全失效。

三、基於資料庫實現方式

資料庫拍他鎖

1、根據名字獲取鎖資訊

2、更新鎖資訊(比如版本,狀態等)佔有鎖

缺點:

1、資料庫的強依賴性,資料庫不可用會導致業務系統不可用。

2、鎖沒有失效時間,解鎖失敗,會使鎖一直存在,其他執行緒無法獲取鎖。

3、鎖只能是非阻塞,插入失敗報錯,執行緒不會進入排隊佇列中,會再次出發獲取鎖的操作。

4、鎖是非重入,同一個執行緒沒有釋放鎖,無法再次獲得該鎖,因為資料庫中鎖的資料已經存在。

解決方案:

1、主從複製

2、定時任務,或者新增上次更新的時間

3、死迴圈insert

4、資料庫存入當前執行緒的資訊。

四、三種分散式鎖優缺點

分散式鎖 優點 缺點
Zookeeper 1、封裝好的框架,容易實現
2、有等待鎖的佇列,提升搶鎖的概率 新增和刪除節點效能低
新增和刪除節點效能低
Redis set和del指令效能較高 1、實現複雜:需要考慮超時、原子性、誤刪等情況
2、沒有等待鎖的佇列,需要在客戶端自旋等待鎖,效率低
資料庫 容易理解 複雜度較高,需要設計表、策略等如果獲取鎖失敗,需要不斷的連結資料庫,查庫操作。

相關文章