分佈式鎖注意點及其實現

來源:SegmentFault 思否社區

作者:skyarthur

分佈式鎖注意點

1)互斥性

在任意時刻只有一個客戶端可以獲取鎖

2)防死鎖

假如一個客戶端在持有鎖的時候崩潰了,沒有釋放鎖,那麼別的客戶端無法獲得鎖,則會造成死鎖,所以要保證客戶端一定會釋放鎖。Redis 中我們可以設置鎖的過期時間來保證不會發生死鎖。

3)持鎖人解鎖

解鈴還須繫鈴人,加鎖和解鎖必須是同一個客戶端,客戶端 A 的線程加的鎖必須是客戶端 A 的線程來解鎖,客戶端不能解開別的客戶端的鎖。

4)可重入

當一個客戶端獲取對象鎖之後,這個客戶端可以再次獲取這個對象上的鎖。

redis 分佈式鎖

實現

Redis 鎖主要利用 Redis 的 setnx 命令。

則加鎖解鎖僞代碼如下:

if (setnx(key, 1) == 1){
    expire(key, 30)
    try {
        //TODO 業務邏輯
    } finally {
        del(key)
    }
}

注意點

SETNX 和 EXPIRE 非原子性

如果 SETNX 成功,在設置鎖超時時間後,服務器掛掉、重啓或網絡問題等,導致 EXPIRE 命令沒有執行,鎖沒有設置超時時間變成死鎖。

解決辦法

  1. setnx 的時候,value 設置和過期時間設置不是原子,可以用 set 命令,redis 版本 2.6.12 以後

  2. lua 腳本,示例如下

if (redis.call('setnx', KEYS[1], ARGV[1]) < 1)
then return 0;
end;
redis.call('expire', KEYS[1], tonumber(ARGV[2]));
return 1;

// 使用實例
EVAL "if (redis.call('setnx',KEYS[1],ARGV[1]) < 1) then return 0; end; redis.call('expire',KEYS[1],tonumber(ARGV[2])); return 1;" 1 key value 100

錯誤解除

如果線程 A 成功獲取到了鎖,並且設置了過期時間 30 秒,但線程 A 執行時間超過了 30 秒,鎖過期自動釋放,此時線程 B 獲取到了鎖;隨後 A 執行完成,線程 A 使用 DEL 命令來釋放鎖,但此時線程 B 加的鎖還沒有執行完成,線程 A 實際釋放的線程 B 加的鎖。

解決辦法

  1. 釋放鎖的時候,只能釋放自己的,不能釋放別人,所以需要 getValue 然後和自己的 id 比較,一致了才能 delete,這裏也需要是原子操作(其實覺得可以通過重試機制來避免)

超時解鎖導致併發

如果線程 A 成功獲取鎖並設置過期時間 30 秒,但線程 A 執行時間超過了 30 秒,鎖過期自動釋放,此時線程 B 獲取到了鎖,線程 A 和線程 B 併發執行。

A、B 兩個線程發生併發顯然是不被允許的,一般有兩種方式解決該問題:

  1. 將過期時間設置足夠長,確保代碼邏輯在鎖釋放之前能夠執行完成。

  2. 爲獲取鎖的線程增加守護線程,爲將要過期但未釋放的鎖增加有效時間。

不可重入

當線程在持有鎖的情況下再次請求加鎖,如果一個鎖支持一個線程多次加鎖,那麼這個鎖就是可重入的。如果一個不可重入鎖被再次加鎖,由於該鎖已經被持有,再次加鎖會失敗。Redis 可通過對鎖進行重入計數,加鎖時加 1,解鎖時減 1,當計數歸 0 時釋放鎖。

主備切換

爲了保證 Redis 的可用性,一般採用主從方式部署。主從數據同步有異步和同步兩種方式,Redis 將指令記錄在本地內存 buffer 中,然後異步將 buffer 中的指令同步到從節點,從節點一邊執行同步的指令流來達到和主節點一致的狀態,一邊向主節點反饋同步情況。

在包含主從模式的集羣部署方式中,當主節點掛掉時,從節點會取而代之,但客戶端無明顯感知。當客戶端 A 成功加鎖,指令還未同步,此時主節點掛掉,從節點提升爲主節點,新的主節點沒有鎖的數據,當客戶端 B 加鎖時就會成功。

集羣腦裂

集羣腦裂指因爲網絡問題,導致 Redis master 節點跟 slave 節點和 sentinel 集羣處於不同的網絡分區,因爲 sentinel 集羣無法感知到 master 的存在,所以將 slave 節點提升爲 master 節點,此時存在兩個不同的 master 節點。Redis Cluster 集羣部署方式同理。

當不同的客戶端連接不同的 master 節點時,兩個客戶端可以同時擁有同一把鎖。如下:

結論

Redis 以其高性能著稱,但使用其實現分佈式鎖來解決併發仍存在一些困難。Redis 分佈式鎖只能作爲一種緩解併發的手段,如果要完全解決併發問題,仍需要數據庫的防併發手段。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/EEImynY2XsUj8b_2u2LI2Q