redis中紅鎖的使用小結(jié)
好的,我們來詳細、深入地探討一下 Redis 紅鎖(RedLock)。
RedLock 是 Redis 官方提出的一種用于在分布式環(huán)境下實現(xiàn)強一致性分布式鎖的算法。它的提出,是為了解決在 Redis ?主從復(fù)制或哨兵模式下,使用單實例 Redis 鎖可能遇到的鎖失效問題。
1. 為什么需要紅鎖?—— 問題的起源
在深入了解紅鎖之前,必須先理解它要解決什么問題。
假設(shè)我們使用單個 Redis 實例實現(xiàn)分布式鎖(通常用 SET key random_value NX PX 30000命令):
- 客戶端 A 在 Master 節(jié)點上成功獲取鎖。
- 在 Master 將鎖數(shù)據(jù)同步到 Slave 節(jié)點之前,Master 宕機了。
- 哨兵機制觸發(fā),一個 Slave 節(jié)點升級為新的 Master。
- 此時,這個新的 Master 節(jié)點上并沒有客戶端 A 持有的鎖。
- 客戶端 B 向新的 Master 節(jié)點申請鎖,成功獲取。此時,?客戶端 A 和客戶端 B 同時認為自己持有了鎖,導(dǎo)致鎖的安全性被破壞。
?核心問題?:在異步復(fù)制的場景下,鎖數(shù)據(jù)在寫入主節(jié)點后,到同步到從節(jié)點之間存在一個時間窗口。在這個窗口內(nèi)主節(jié)點故障,會導(dǎo)致鎖數(shù)據(jù)的丟失。
紅鎖的目標(biāo)就是消除對單個 Redis 實例的依賴,從而避免這類問題。
2. 紅鎖算法(RedLock Algorithm)的核心思想
紅鎖的基本思想非常直觀:??“不要把所有雞蛋放在一個籃子里”?。
它要求客戶端向一個獨立的、無主從關(guān)系的 Redis 實例集群中的多數(shù)(N/2+1)個節(jié)點依次申請鎖,只有當(dāng)從多數(shù)節(jié)點都獲取鎖成功,并且總耗時小于鎖的有效時間,才算最終加鎖成功。
算法前提條件
?部署多個 Redis Master 節(jié)點?:這些節(jié)點必須是完全獨立的,相互之間沒有數(shù)據(jù)同步關(guān)系(例如,不是同一個哨兵或集群下的節(jié)點)。建議至少 5 個節(jié)點,這樣可以容忍其中 2 個節(jié)點故障,從而保證系統(tǒng)的可用性。
算法步驟詳解
假設(shè)我們有 N 個 Redis 節(jié)點(例如 N=5)。
?第一步:獲取當(dāng)前時間?
客戶端在開始獲取鎖之前,先記錄一個開始時間 start_time。
?第二步:依次向所有 N 個節(jié)點申請鎖?
客戶端使用相同的鍵名和隨機值,依次向 5 個 Redis 實例發(fā)送鎖申請命令(SET lock_name my_random_value NX PX 30000)。
為了減少因為某個節(jié)點故障而造成的長時間等待,可以為每個節(jié)點的請求設(shè)置一個遠小于鎖超時時間的網(wǎng)絡(luò)超時時間(例如,鎖超時 10 秒,網(wǎng)絡(luò)超時 50-100 毫秒)。如果一個節(jié)點沒有響應(yīng),應(yīng)盡快嘗試下一個節(jié)點。
?第三步:計算獲取鎖的總耗時?
當(dāng)客戶端從所有節(jié)點都收到響應(yīng)(無論是成功還是失?。┖螅涗浗Y(jié)束時間 end_time。計算總耗時:total_time = end_time - start_time。
?第四步:檢查鎖是否獲取成功?
客戶端需要同時滿足以下兩個條件,才認為鎖獲取成功:
- ?多數(shù)派原則?:客戶端從至少
N/2 + 1個節(jié)點(對于 5 個節(jié)點,就是 3 個)上成功獲取了鎖。 - ?有效性檢查?:獲取鎖的總耗時
total_time必須小于鎖的自動釋放時間(TTL)。例如,鎖的 TTL 是 10 秒,而total_time是 2 秒,那么是有效的。如果total_time是 12 秒,則無效。
?為什么需要條件 2??? 這是為了防止客戶端在獲取鎖的過程中耗時太久,導(dǎo)致最早申請到的那些鎖在客戶端開始執(zhí)行關(guān)鍵代碼前就已經(jīng)過期了。
?如果鎖獲取失敗?:客戶端必須向所有 Redis 節(jié)點發(fā)起釋放鎖的請求?(即執(zhí)行 Lua 腳本,檢查隨機值并刪除鎖)。即使某些節(jié)點返回失?。ū热缢緛砭蜎]加鎖成功),也需要嘗試釋放,以確保清理現(xiàn)場。
3. 釋放鎖
釋放鎖的過程相對簡單:客戶端需要向第一步中嘗試獲取鎖的所有 N 個節(jié)點發(fā)送釋放命令。
釋放命令必須使用 Lua 腳本保證原子性:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end這樣做是為了確保只有鎖的持有者才能釋放鎖,避免誤刪其他客戶端創(chuàng)建的鎖。
4. 紅鎖的爭議與局限性(非常重要?。?/h2>
RedLock 自提出以來,就在分布式系統(tǒng)領(lǐng)域引發(fā)了激烈的討論,特別是來自 Martin Kleppmann(《數(shù)據(jù)密集型應(yīng)用系統(tǒng)設(shè)計》作者)的挑戰(zhàn)。理解這些爭議點對于正確使用紅鎖至關(guān)重要。
主要爭議點:
?對系統(tǒng)時鐘(時鐘跳躍)的敏感性?
- ?場景?:假設(shè)客戶端 1 持有鎖。某個 Redis 節(jié)點因為系統(tǒng)時鐘被調(diào)整(例如通過 NTP 同步或人為修改),導(dǎo)致其上的鎖提前過期。
- ?問題?:客戶端 2 向這個節(jié)點申請鎖,可能成功。如果客戶端 2 又從其他節(jié)點成功獲取了足夠多的鎖,那么紅鎖算法就會認為客戶端 2 獲得了鎖,從而導(dǎo)致兩個客戶端同時進入臨界區(qū)。
- ?紅鎖的辯護?:Redis 作者 Antirez 認為,應(yīng)該通過合理的運維手段禁止這種跳躍式的時鐘調(diào)整,而使用“慢速收斂”的時鐘同步方式。
?GC Pause(垃圾回收暫停)或進程暫停帶來的安全性問題?
?場景(Martin 描述的著名例子)??:
- 客戶端 1 成功獲得紅鎖,并開始執(zhí)行關(guān)鍵代碼。
- 在執(zhí)行過程中,發(fā)生了長時間的 GC Pause(例如 30 秒),導(dǎo)致客戶端 1 的進程被“凍結(jié)”。
- 在此期間,客戶端 1 持有的鎖因為超時(TTL 到期)而被所有 Redis 節(jié)點自動釋放。
- 客戶端 2 成功獲得了紅鎖,并開始執(zhí)行關(guān)鍵代碼。
- 客戶端 1 從 GC Pause 中恢復(fù),繼續(xù)執(zhí)行關(guān)鍵代碼。此時,?客戶端 1 和客戶端 2 再次同時進入了臨界區(qū)。
?問題的本質(zhì)?:紅鎖(以及任何基于超時的鎖)無法區(qū)分“客戶端業(yè)務(wù)邏輯執(zhí)行緩慢”和“客戶端進程已崩潰”這兩種情況。它依賴于超時機制,而超時機制在存在長時間進程暫停時就會失效。
?解決方案的討論?:Martin 提出需要使用一種能夠在共享存儲中留下“圍欄令牌”(fencing token)?? 的鎖服務(wù)??蛻舳嗽趯憯?shù)據(jù)時,需要檢查一個單調(diào)遞增的令牌,以確保自己的操作是在最新的鎖狀態(tài)下進行的。這實際上要求共享資源層(如 Zookeeper、etcd)提供額外的協(xié)調(diào)能力。
5. 紅鎖的使用建議
考慮到上述爭議,你應(yīng)該在以下情況下考慮使用紅鎖:
- ?對一致性要求極高?:你確實需要一把強一致的鎖,并且可以接受紅鎖帶來的性能下降(因為需要與多個節(jié)點通信)。
- ?可以控制運維環(huán)境?:你能夠確保 Redis 節(jié)點所在的機器不會發(fā)生劇烈的時鐘漂移。
- ?理解其局限性?:你清楚地知道,在極端情況下(如長時間的進程暫停),紅鎖仍然無法提供 100% 的安全保證。對于大多數(shù)業(yè)務(wù)場景,這種極端情況的發(fā)生概率和其帶來的風(fēng)險是可以接受的。
?替代方案?:
- ?對于高可用要求不極端的場景?:使用 Redis 主從+哨兵模式,并接受在主從切換的極小時間窗口內(nèi)可能出現(xiàn)的鎖失效問題。很多業(yè)務(wù)場景下,這種風(fēng)險是可接受的。
- ?對于必須強一致的場景?:考慮使用專門為分布式協(xié)調(diào)而設(shè)計的系統(tǒng),如 ?ZooKeeper? 或 ?etcd。這些系統(tǒng)使用了共識算法(如 Zab、Raft),它們本身就是為了在分布式環(huán)境下提供強一致性和容錯性而設(shè)計的,實現(xiàn)分布式鎖是它們的原生強項,通常能提供比紅鎖更可靠的安全保證。
總結(jié)
特性 | 描述 |
|---|---|
?目標(biāo)? | 在分布式 Redis 環(huán)境下實現(xiàn)一個更安全、強一致的分布式鎖。 |
?核心思想? | 向多個獨立的 Redis 主節(jié)點申請鎖,遵循“多數(shù)派”原則。 |
?優(yōu)點? | 解決了單點 Redis 和主從架構(gòu)下因異步復(fù)制導(dǎo)致的鎖失效問題。 |
?缺點/爭議? | 1. 對系統(tǒng)時鐘漂移敏感。 |
?適用場景? | 對鎖的一致性要求非常高,且愿意為了一致性犧牲部分性能和增加復(fù)雜度的場景。 |
?替代方案? | Redis 主從鎖(可接受風(fēng)險)、ZooKeeper、etcd。 |
到此這篇關(guān)于redis中紅鎖的使用小結(jié)的文章就介紹到這了,更多相關(guān)redis 紅鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問題解決
本文主要介紹了攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
關(guān)于Redis網(wǎng)絡(luò)模型的源碼詳析
這篇文章主要給大家介紹了關(guān)于Redis網(wǎng)絡(luò)模型的源碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Windows系統(tǒng)安裝redis數(shù)據(jù)庫
這篇文章介紹了Windows系統(tǒng)安裝redis數(shù)據(jù)庫的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03

