Redisson 分布式鎖源碼 09:RedLock 紅鎖的故事


前言

RedLock 紅鎖,是分布式鎖中必須要了解的一個概念。

所以本文會先介紹什么是 RedLock,當大家對 RedLock 有一個基本的了解。然后再看 Redisson 中是如何實現 RedLock 的。

在文章開頭先說明 Redisson RedLock 建議不要使用!!!
在文章開頭先說明 Redisson RedLock 建議不要使用!!!
在文章開頭先說明 Redisson RedLock 建議不要使用!!!

重要的事情重復三遍!

什么是 RedLock?

RedLock,這塊可以從網上搜到很多資料,本文也簡單介紹下,當做掃盲。

單實例加鎖

SET resource_name my_random_value NX PX 30000

對於單實例 Redis 只需要使用這個命令即可。

  • NX:僅在不存在 key 的時候才能被執行成功;
  • PX:失效時間,傳入 30000,就是 30s 后自動釋放鎖;
  • my_random_value:就是隨機值,可以是線程號之類的。主要是為了更安全的釋放鎖,釋放鎖的時候使用腳本告訴 Redis: 只有 key 存在並且存儲的值和我指定的值一樣才能刪除成功

可以通過以下 Lua 腳本實現鎖釋放:

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

為什么要設置隨機值?

主要是為了防止鎖被其他客戶端刪除。有這么一種情況:

  1. 客戶端 A 獲得了鎖,還沒有執行結束,但是鎖超時自動釋放了;
  2. 客戶端 B 此時過來,是可以獲得鎖的,加鎖成功;
  3. 此時,客戶端 A 執行結束了,要去釋放鎖,如果不對比隨機值,就會把客戶端 B 的鎖給釋放了。

當然前面看過 Redisson 的處理,這個 my_random_value 存放的是 UUID:ThreadId 組合成的一個類似 931573de-903e-42fd-baa7-428ebb7eda80:1 的字符串。

當鎖遇到故障轉移

單實例肯定不是很可靠吧?加鎖成功之后,結果 Redis 服務宕機了,這不就涼涼~

這時候會提出來將 Redis 主從部署。即使是主從,也是存在巧合的!

主從結構中存在明顯的競態:

  1. 客戶端 A 從 master 獲取到鎖
  2. 在 master 將鎖同步到 slave 之前,master 宕掉了。
  3. slave 節點被晉級為 master 節點
  4. 客戶端 B 取得了同一個資源被客戶端 A 已經獲取到的另外一個鎖。安全失效!

有時候程序就是這么巧,比如說正好一個節點掛掉的時候,多個客戶端同時取到了鎖。如果你可以接受這種小概率錯誤,那用這個基於復制的方案就完全沒有問題。

那我使用集群呢?

如果還記得前面的內容,應該是知道對集群進行加鎖的時候,其實是通過 CRC16 的 hash 函數來對 key 進行取模,將結果路由到預先分配過 slot 的相應節點上。

發現其實還是發到單個節點上的

RedLock 概念

這時候 Redis 作者提出了 RedLock 的概念

總結一下就是對集群的每個節點進行加鎖,如果大多數(N/2+1)加鎖成功了,則認為獲取鎖成功。

RedLock 的問題

看着 RedLock 好像是解決問題了:

  1. 客戶端 A 鎖住了集群的大多數(一半以上);
  2. 客戶端 B 也要鎖住大多數;
  3. 這里肯定會沖突,所以 客戶端 B 加鎖失敗。

那實際解決問題了么?

推薦大家閱讀兩篇文章:

最終,兩方各持己見,沒有得出結論。

鑒於本文主要是分析 Redisson 的 RedLock,就不做額外贅述,感興趣的小伙伴可以自己閱讀。

Redisson 中 RedLock 源碼

這里會簡要分析一下 Redisson 中 RedLock 的源碼,然后會介紹為什么文章開頭不建議大家使用 Redisson 的 RedLock。

使用方式

乍一看,感覺和聯鎖 MultiLock 的使用方式很像啊!

實際上就是很像,RedissonRedLock 完全是 RedissonMultiLock 的子類嘛!

只不過是重寫 failedLocksLimit 方法。

在 MultiLock 中,要所有的鎖都鎖成功才可以。

在 RedLock 中,要一半以上的鎖成功。

剩余部分源碼都和 MultiLock 一樣,就不在重復描述了。

Redisson 中 RedLock 的問題

1、加鎖 key 的問題

閱讀源碼之前,有一個很大的疑問,我加鎖 lock1、lock2、lock3,但是 RedissonRedLock 是如何保證這三個 key 是在歸屬於 Redis 集群中不同的 master 呢?

因為按照 RedLock 的理論,是需要在半數以上的 master 節點加鎖成功

閱讀完源碼之后,發現 RedissonRedLock 完全是 RedissonMultiLock 的子類,只是重寫了 failedLocksLimit 方法,保證半數以上加鎖成功即可。

所以這三個 key,是需要用戶來保證分散在不同的節點上的。

https://github.com/redisson/redisson/issues/2436

在 Redisson 的 issues 也有同樣的小伙伴提出這個問題,相關開發者給出的回復是用戶來保證 key 分散在不同的 master 上。

https://github.com/redisson/redisson/issues/2127

更有小伙伴提出使用 5 個客戶端。

那我使用 5 個單節點的客戶端,然后再使用紅鎖,聽着好像是可以的,並且 RedissonRedLock 可以這樣使用。

但是那和 Redis 集群還有啥關系啊!

所以依然沒有解決我的問題,還是需要用戶自己來“手工定位鎖”。

手工定位鎖,這個…… 我考慮了下,還是不用 RedLock 吧!

當然 DarrenJiang1990 同學應該是懷着打破砂鍋問到底的心情,又來了一篇 issue。

https://github.com/redisson/redisson/issues/2437

意思就是:不要關閉我的 issues,在 #2436 中說可以“手工定位鎖”,但是我要怎么手工定位鎖。

后來這個 issue 在 10 月才回復。

2、RedissonRedLock 被棄用

是的,沒有看錯,現在 RedissonRedLock 已經被啟用了。

如果是看的英文文檔,就會發現:

而中文文檔,應該是沒有及時更新。

來看看更新記錄:

再找一找 issue:

https://github.com/redisson/redisson/issues/2669

Redisson 的開發者認為 Redis 的紅鎖也存在爭議(前文介紹的那個爭議),但是為了保證可用性,RLock 對象執行的每個 Redis 命令執行都通過 Redis 3.0 中引入的 WAIT 命令進行同步。

WAIT 命令會阻塞當前客戶端,直到所有以前的寫命令都成功的傳輸並被指定數量的副本確認。如果達到以毫秒為單位指定的超時,則即使尚未達到指定數量的副本,該命令也會返回。
WAIT 命令同步復制也並不能保證強一致性,不過在主節點宕機之后,只不過會盡可能的選擇最佳的副本(slaves)

源碼在這一部分。

看源碼,同時發送了一個 WAIT 1 1000 到 Redis。

結論

Redisson RedLock 是基於聯鎖 MultiLock 實現的,但是使用過程中需要自己判斷 key 落在哪個節點上,對使用者不是很友好。

Redisson RedLock 已經被棄用,直接使用普通的加鎖即可,會基於 wait 機制將鎖同步到從節點,但是也並不能保證一致性。僅僅是最大限度的保證一致性。

相關推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM