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

前言
RedLock 紅鎖,是分布式鎖中必須要了解的一個(gè)概念。
所以本文會先介紹什么是 RedLock,當(dāng)大家對 RedLock 有一個(gè)基本的了解。然后再看 Redisson 中是如何實(shí)現(xiàn) RedLock 的。
在文章開頭先說明 Redisson RedLock 建議不要使用!?。?/strong>
1
什么是 RedLock?
RedLock[1],這塊可以從網(wǎng)上搜到很多資料,本文也簡單介紹下,當(dāng)做掃盲。
單實(shí)例加鎖
SET resource_name my_random_value NX PX 30000
對于單實(shí)例 Redis 只需要使用這個(gè)命令即可。
NX:僅在不存在 key 的時(shí)候才能被執(zhí)行成功; PX:失效時(shí)間,傳入 30000,就是 30s 后自動釋放鎖; my_random_value:就是隨機(jī)值,可以是線程號之類的。主要是為了更安全的釋放鎖,釋放鎖的時(shí)候使用腳本告訴 Redis: 只有 key 存在并且存儲的值和我指定的值一樣才能刪除成功。
可以通過以下 Lua 腳本實(shí)現(xiàn)鎖釋放:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
為什么要設(shè)置隨機(jī)值?
主要是為了防止鎖被其他客戶端刪除。有這么一種情況:
客戶端 A 獲得了鎖,還沒有執(zhí)行結(jié)束,但是鎖超時(shí)自動釋放了; 客戶端 B 此時(shí)過來,是可以獲得鎖的,加鎖成功; 此時(shí),客戶端 A 執(zhí)行結(jié)束了,要去釋放鎖,如果不對比隨機(jī)值,就會把客戶端 B 的鎖給釋放了。
當(dāng)然前面看過 Redisson 的處理,這個(gè) my_random_value 存放的是 UUID:ThreadId 組合成的一個(gè)類似 931573de-903e-42fd-baa7-428ebb7eda80:1 的字符串。
當(dāng)鎖遇到故障轉(zhuǎn)移
單實(shí)例肯定不是很可靠吧?加鎖成功之后,結(jié)果 Redis 服務(wù)宕機(jī)了,這不就涼涼~
這時(shí)候會提出來將 Redis 主從部署。即使是主從,也是存在巧合的!
主從結(jié)構(gòu)中存在明顯的競態(tài):
客戶端 A 從 master 獲取到鎖 在 master 將鎖同步到 slave 之前,master 宕掉了。 slave 節(jié)點(diǎn)被晉級為 master 節(jié)點(diǎn) 客戶端 B 取得了同一個(gè)資源被客戶端 A 已經(jīng)獲取到的另外一個(gè)鎖。安全失效!
有時(shí)候程序就是這么巧,比如說正好一個(gè)節(jié)點(diǎn)掛掉的時(shí)候,多個(gè)客戶端同時(shí)取到了鎖。如果你可以接受這種小概率錯(cuò)誤,那用這個(gè)基于復(fù)制的方案就完全沒有問題。
那我使用集群呢?
如果還記得前面的內(nèi)容,應(yīng)該是知道對集群進(jìn)行加鎖的時(shí)候,其實(shí)是通過 CRC16 的 hash 函數(shù)來對 key 進(jìn)行取模,將結(jié)果路由到預(yù)先分配過 slot 的相應(yīng)節(jié)點(diǎn)上。
發(fā)現(xiàn)其實(shí)還是發(fā)到單個(gè)節(jié)點(diǎn)上的!
RedLock 概念
這時(shí)候 Redis 作者提出了 RedLock 的概念

總結(jié)一下就是對集群的每個(gè)節(jié)點(diǎn)進(jìn)行加鎖,如果大多數(shù)(N/2+1)加鎖成功了,則認(rèn)為獲取鎖成功。
RedLock 的問題
看著 RedLock 好像是解決問題了:
客戶端 A 鎖住了集群的大多數(shù)(一半以上); 客戶端 B 也要鎖住大多數(shù); 這里肯定會沖突,所以 客戶端 B 加鎖失敗。
那實(shí)際解決問題了么?
推薦大家閱讀兩篇文章:
Martin Kleppmann:How to do distributed locking[2]
Salvatore(Redis 作者):Is Redlock safe?[3]
最終,兩方各持己見,沒有得出結(jié)論。
鑒于本文主要是分析 Redisson 的 RedLock,就不做額外贅述,感興趣的小伙伴可以自己閱讀。
2
Redisson 中 RedLock 源碼
這里會簡要分析一下 Redisson 中 RedLock 的源碼,然后會介紹為什么文章開頭不建議大家使用 Redisson 的 RedLock。
使用方式

乍一看,感覺和聯(lián)鎖 MultiLock 的使用方式很像啊!

實(shí)際上就是很像,RedissonRedLock 完全是 RedissonMultiLock 的子類嘛!
只不過是重寫 failedLocksLimit 方法。
在 MultiLock 中,要所有的鎖都鎖成功才可以。
在 RedLock 中,要一半以上的鎖成功。
剩余部分源碼都和 MultiLock 一樣,就不在重復(fù)描述了。
Redisson 中 RedLock 的問題
加鎖 key 的問題
閱讀源碼之前,有一個(gè)很大的疑問,我加鎖 lock1、lock2、lock3,但是 RedissonRedLock 是如何保證這三個(gè) key 是在歸屬于 Redis 集群中不同的 master 呢?
因?yàn)榘凑?RedLock 的理論,是需要在半數(shù)以上的 master 節(jié)點(diǎn)加鎖成功。
閱讀完源碼之后,發(fā)現(xiàn) RedissonRedLock 完全是 RedissonMultiLock 的子類,只是重寫了 failedLocksLimit 方法,保證半數(shù)以上加鎖成功即可。
所以這三個(gè) key,是需要用戶來保證分散在不同的節(jié)點(diǎn)上的。

https://github.com/redisson/redisson/issues/2436
在 Redisson 的 issues 也有同樣的小伙伴提出這個(gè)問題,相關(guān)開發(fā)者給出的回復(fù)是用戶來保證 key 分散在不同的 master 上。
https://github.com/redisson/redisson/issues/2127
更有小伙伴提出使用 5 個(gè)客戶端。

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

RedissonRedLock 被棄用
是的,沒有看錯(cuò),現(xiàn)在 RedissonRedLock 已經(jīng)被啟用了。
如果是看的英文文檔,就會發(fā)現(xiàn):

而中文文檔,應(yīng)該是沒有及時(shí)更新。
來看看更新記錄:

再找一找 issue:
https://github.com/redisson/redisson/issues/2669

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

源碼在這一部分。

看源碼,同時(shí)發(fā)送了一個(gè) WAIT 1 1000 到 Redis。
3
結(jié)論
Redisson RedLock 是基于聯(lián)鎖 MultiLock 實(shí)現(xiàn)的,但是使用過程中需要自己判斷 key 落在哪個(gè)節(jié)點(diǎn)上,對使用者不是很友好。
Redisson RedLock 已經(jīng)被棄用,直接使用普通的加鎖即可,會基于 wait 機(jī)制將鎖同步到從節(jié)點(diǎn),但是也并不能保證一致性。僅僅是最大限度的保證一致性。
引用鏈接:
RedLock: http://redis.cn/topics/distlock.html
[2]Martin Kleppmann:How to do distributed locking: https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
[3]Salvatore:Is Redlock safe?: http://antirez.com/news/101
- <End /> -
歷史文章 | 相關(guān)推薦

