前言
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
為什么要設置隨機值?
主要是為了防止鎖被其他客戶端刪除。有這么一種情況:
- 客戶端 A 獲得了鎖,還沒有執行結束,但是鎖超時自動釋放了;
- 客戶端 B 此時過來,是可以獲得鎖的,加鎖成功;
- 此時,客戶端 A 執行結束了,要去釋放鎖,如果不對比隨機值,就會把客戶端 B 的鎖給釋放了。
當然前面看過 Redisson 的處理,這個 my_random_value 存放的是 UUID:ThreadId 組合成的一個類似 931573de-903e-42fd-baa7-428ebb7eda80:1 的字符串。
當鎖遇到故障轉移
單實例肯定不是很可靠吧?加鎖成功之后,結果 Redis 服務宕機了,這不就涼涼~
這時候會提出來將 Redis 主從部署。即使是主從,也是存在巧合的!
主從結構中存在明顯的競態:
- 客戶端 A 從 master 獲取到鎖
- 在 master 將鎖同步到 slave 之前,master 宕掉了。
- slave 節點被晉級為 master 節點
- 客戶端 B 取得了同一個資源被客戶端 A 已經獲取到的另外一個鎖。安全失效!
有時候程序就是這么巧,比如說正好一個節點掛掉的時候,多個客戶端同時取到了鎖。如果你可以接受這種小概率錯誤,那用這個基於復制的方案就完全沒有問題。
那我使用集群呢?
如果還記得前面的內容,應該是知道對集群進行加鎖的時候,其實是通過 CRC16 的 hash 函數來對 key 進行取模,將結果路由到預先分配過 slot 的相應節點上。
發現其實還是發到單個節點上的!
RedLock 概念
這時候 Redis 作者提出了 RedLock 的概念

總結一下就是對集群的每個節點進行加鎖,如果大多數(N/2+1)加鎖成功了,則認為獲取鎖成功。
RedLock 的問題
看着 RedLock 好像是解決問題了:
- 客戶端 A 鎖住了集群的大多數(一半以上);
- 客戶端 B 也要鎖住大多數;
- 這里肯定會沖突,所以 客戶端 B 加鎖失敗。
那實際解決問題了么?
推薦大家閱讀兩篇文章:
-
Martin Kleppmann:How to do distributed locking
-
Salvatore(Redis 作者):Is Redlock safe?
最終,兩方各持己見,沒有得出結論。
鑒於本文主要是分析 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,是需要用戶來保證分散在不同的節點上的。

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

那我使用 5 個單節點的客戶端,然后再使用紅鎖,聽着好像是可以的,並且 RedissonRedLock 可以這樣使用。
但是那和 Redis 集群還有啥關系啊!
所以依然沒有解決我的問題,還是需要用戶自己來“手工定位鎖”。
手工定位鎖,這個…… 我考慮了下,還是不用 RedLock 吧!
當然 DarrenJiang1990 同學應該是懷着打破砂鍋問到底的心情,又來了一篇 issue。
https://github.com/redisson/redisson/issues/2437
意思就是:不要關閉我的 issues,在 #2436 中說可以“手工定位鎖”,但是我要怎么手工定位鎖。
后來這個 issue 在 10 月才回復。

2、RedissonRedLock 被棄用
是的,沒有看錯,現在 RedissonRedLock 已經被啟用了。
如果是看的英文文檔,就會發現:

而中文文檔,應該是沒有及時更新。
來看看更新記錄:

再找一找 issue:

Redisson 的開發者認為 Redis 的紅鎖也存在爭議(前文介紹的那個爭議),但是為了保證可用性,RLock 對象執行的每個 Redis 命令執行都通過 Redis 3.0 中引入的 WAIT 命令進行同步。
WAIT 命令會阻塞當前客戶端,直到所有以前的寫命令都成功的傳輸並被指定數量的副本確認。如果達到以毫秒為單位指定的超時,則即使尚未達到指定數量的副本,該命令也會返回。
WAIT 命令同步復制也並不能保證強一致性,不過在主節點宕機之后,只不過會盡可能的選擇最佳的副本(slaves)

源碼在這一部分。

看源碼,同時發送了一個 WAIT 1 1000 到 Redis。
結論
Redisson RedLock 是基於聯鎖 MultiLock 實現的,但是使用過程中需要自己判斷 key 落在哪個節點上,對使用者不是很友好。
Redisson RedLock 已經被棄用,直接使用普通的加鎖即可,會基於 wait 機制將鎖同步到從節點,但是也並不能保證一致性。僅僅是最大限度的保證一致性。
