面試必考法瑟,高可用的分布式鎖如何設(shè)計

分布式鎖定義

分布式鎖在分布式環(huán)境下,鎖定全局唯一公共資源唁奢,表現(xiàn)為:

  • 請求串行化

  • 互斥性

第一步是上鎖的資源目標(biāo)霎挟,是鎖定全局唯一公共資源,只有是全局唯一的資源才存在多個線程或服務(wù)競爭的情況驮瞧。

互斥性表現(xiàn)為一個資源的隔離級別串行化氓扛,如果對照單機事務(wù) ACID 的隔離性來說,互斥性的事務(wù)隔離級別是 SERLALIZABLE,屬于最高的隔離級別采郎。

事務(wù)隔離級別:

  • DEFAULT

  • READ_UNCOMMITTED

  • READ_COMMITED

  • REPEATABLE_READ

  • SERLALIZABLE

分布式鎖目的

分布式鎖的目的如下:

  • 解決業(yè)務(wù)層冪等性

  • 解決 MQ 消費端多次接受同一消息

  • 確保串行|隔離級別

  • 多臺機器同時執(zhí)行定時任務(wù)

尋找唯一資源進行上鎖

例子:

  1. 防止用戶重復(fù)下單 共享資源進行上鎖的對象 : 【用戶id】
  2. 訂單生成后發(fā)送MQ給消費者進行積分的添加 尋找上鎖的對象 :【訂單id】
  3. 用戶已經(jīng)創(chuàng)建訂單千所,準(zhǔn)備對訂單進行支付,同時商家在對這個訂單進行改價 尋找上鎖對象 : 【訂單id】

基于 Redis 分布式鎖

Redis 單線程串行處理天然就是解決串行化問題蒜埋,用來解決分布式鎖是再適合不過淫痰。

實現(xiàn)方式:

setnx key value Expire_time //獲取到鎖 返回 1 , 獲取失敗 返回 0

存在問題如下:

鎖時間不可控

Redis 只能在 Setnx 指定一個鎖的超時時間整份,假設(shè)初始設(shè)定鎖的時間是 10 秒鐘待错,但是業(yè)務(wù)獲取到鎖跑了 20 秒鐘,在 10 秒鐘之后烈评,如果又有一個業(yè)務(wù)可以獲取到相同的一把鎖火俄。

這個時候可能就存在兩個相同的業(yè)務(wù)都獲取得到鎖的問題,并且兩個業(yè)務(wù)處在并行階段讲冠。也就是第一個獲取鎖的業(yè)務(wù)無法對自身的鎖進行續(xù)租瓜客。

單點連接超時問題

Redis 的 Client 與 Server 端并沒有維持心跳的機制,如果在連接中出現(xiàn)問題竿开,Client 會得到一個超時的回饋谱仪。

主從問題

Redis 的集群實際上在 CAP 模式中是處在與 AP 的模型,保證可用性否彩。在主從復(fù)制中“主”有數(shù)據(jù)疯攒,但可能“從”還沒有數(shù)據(jù)。這個時候列荔,一旦主掛掉或者網(wǎng)絡(luò)抖動等各種原因敬尺,可能會切換到“從”節(jié)點。

這個時候有可能會導(dǎo)致兩個業(yè)務(wù)線程同時的獲取到兩把鎖:

1.業(yè)務(wù)線程-1:向主節(jié)點請求鎖

2.業(yè)務(wù)線程-1:獲取鎖

3.業(yè)務(wù)線程-1:獲取到鎖并開始執(zhí)行業(yè)務(wù)

4.這個時候 Redis 剛生成的鎖在主從之間還未進行同步

5.Redis 這時候主節(jié)點掛掉了

6.Redis 的從節(jié)點升級為主節(jié)點

7.業(yè)務(wù)線程-2:向新的主節(jié)點請求鎖

8.業(yè)務(wù)線程-2:獲取到新的主節(jié)點返回的鎖

9.業(yè)務(wù)線程-2:獲取到鎖開始執(zhí)行業(yè)務(wù)

10.這個時候業(yè)務(wù)線程-1和業(yè)務(wù)線程-2同時在執(zhí)行任務(wù)

Redlock

上述的問題其實并不是 Redis 的缺陷贴浙,只是 Redis 采用了 AP 模型筷转,它本身無法確保我們對一致性的要求。

Redis 官方推薦 Redlock 算法來保證悬而,問題是 Redlock 至少需要三個 Redis 主從實例來實現(xiàn)呜舒,維護成本比較高。

相當(dāng)于 Redlock 使用三個 Redis 集群實現(xiàn)了自己的另一套一致性算法笨奠,比較繁瑣袭蝗,在業(yè)界也使用得比較少。

能不能使用 Redis 作為分布式鎖

能不能使用 Redis 作為分布式鎖般婆,這個本身就不是 Redis 的問題到腥,還是取決于業(yè)務(wù)場景,我們先要自己確認我們的場景是適合 AP 還是 CP蔚袍。

如果在社交發(fā)帖等場景下乡范,我們并沒有非常強的事務(wù)一致性問題配名,Redis 提供給我們高性能的 AP 模型是非常適合的。

但如果是交易類型晋辆,對數(shù)據(jù)一致性非常敏感的場景渠脉,我們可能要尋找一種更加適合的 CP 模型。

Redis 可能作為高可用的分布式鎖并不合適瓶佳,我們需要確立高可用分布式鎖的設(shè)計目標(biāo)芋膘。

高可用分布式鎖設(shè)計目標(biāo)

高可用分布式鎖的設(shè)計目標(biāo)如下:

  • 強一致性,是 CP 模型

  • 服務(wù)高可用霸饲,不存在單點問題

  • 鎖能夠續(xù)租和自動釋放

  • 業(yè)務(wù)接入簡單

三種分布式鎖方案對比

常用的三種分布式鎖方案對比如下圖:

基于 Zookeeper 分布式鎖

剛剛也分析過为朋,Redis 其實無法確保數(shù)據(jù)的一致性,先來看 Zookeeper 是否合適作為我們需要的分布式鎖厚脉。

首先 ZK 的模式是 CP 模型习寸,也就是說,當(dāng) ZK 鎖提供給我們進行訪問的時候傻工,在 ZK 集群中能確保這把鎖在 ZK 的每一個節(jié)點都存在融涣。

這個實際上是 ZK 的 Leader 通過二階段提交寫請求來保證的,這個也是 ZK 的集群規(guī)模大了的一個瓶頸點精钮。

ZK 鎖實現(xiàn)的原理

說 ZK 的鎖問題之前先看看 Zookeeper 中的幾個特性,這幾個特性構(gòu)建了 ZK 的一把分布式鎖剃斧。

Zookeeper 中的幾個特性如下:

  • 有序節(jié)點轨香,當(dāng)在一個父目錄下如 /lock 下創(chuàng)建有序節(jié)點,節(jié)點會按照嚴格的先后順序創(chuàng)建出自節(jié)點 lock000001幼东,lock000002,lock0000003,以此類推矾利,有序節(jié)點能嚴格保證各個自節(jié)點按照排序命名生成拷肌。

  • 臨時節(jié)點,客戶端建立了一個臨時節(jié)點简逮,在客戶端的會話結(jié)束或會話超時球散,Zookeeper 會自動刪除該節(jié)點 ID。

  • 事件監(jiān)聽散庶,在讀取數(shù)據(jù)時蕉堰,我們可以對節(jié)點設(shè)置監(jiān)聽,當(dāng)節(jié)點的數(shù)據(jù)發(fā)生變化(1 節(jié)點創(chuàng)建悲龟,2 節(jié)點刪除屋讶,3 節(jié)點數(shù)據(jù)變動,4 子節(jié)點變動)時须教,Zookeeper 會通知客戶端皿渗。

結(jié)合這幾個特點,來看下 ZK 是怎么組合分布式鎖:

  • 業(yè)務(wù)線程-1,業(yè)務(wù)線程-2 分別向 ZK 的 /lock 目錄下乐疆,申請創(chuàng)建有序的臨時節(jié)點划乖。

  • 業(yè)務(wù)線程-1 搶到 /lock0001 的文件,也就是在整個目錄下最小序的節(jié)點诀拭,也就是線程-1 獲取到了鎖迁筛。

  • 業(yè)務(wù)線程-2 只能搶到 /lock0002 的文件,并不是最小序的節(jié)點耕挨,線程 2 未能獲取鎖细卧。

  • 業(yè)務(wù)線程-1 與 lock0001 建立了連接,并維持了心跳筒占,維持的心跳也就是這把鎖的租期贪庙。

  • 當(dāng)業(yè)務(wù)線程-1 完成了業(yè)務(wù),將釋放掉與 ZK 的連接翰苫,也就是釋放了這把鎖止邮。

ZK 分布式鎖的代碼實現(xiàn)

ZK 官方提供的客戶端并不支持分布式鎖的直接實現(xiàn),我們需要自己寫代碼去利用 ZK 的這幾個特性去進行實現(xiàn):

ZK 分布式鎖客戶端假死的問題

客戶端創(chuàng)建了臨時有序節(jié)點并建立了事件監(jiān)聽奏窑,就可以讓業(yè)務(wù)線程與 ZK 維持心跳导披,這個心跳也就是這把鎖的租期。

當(dāng)客戶端的業(yè)務(wù)線程完成了執(zhí)行就把節(jié)點進行刪除埃唯,也就釋放了這把鎖撩匕,不過中間也可能存在問題:

  • 客戶端掛掉。因為注冊的是臨時節(jié)點墨叛,客戶端掛掉止毕,ZK 會進行感知,也就會把這個臨時節(jié)點刪除漠趁,鎖也就隨著釋放扁凛。

  • 業(yè)務(wù)線程假死。業(yè)務(wù)線程并沒有消息闯传,而是一個假死狀態(tài)谨朝,(例如死循環(huán),死鎖甥绿,超長 GC)叠必,這個時候鎖會被一直霸占不能釋放,這個問題需要從兩個方面進行解決妹窖。

第一個是本身業(yè)務(wù)代碼的問題纬朝,為何會出現(xiàn)死循環(huán),死鎖等問題骄呼;第二個是對鎖的異常監(jiān)控問題共苛,這個其實也是微服務(wù)治理的一個方面判没。

ZK 分布式鎖的 GC 問題

image

剛剛說了 ZK 鎖的維持是靠 ZK 和客戶端的心跳進行維持,如果客戶端出現(xiàn)了長時間的 GC 會出現(xiàn)什么狀況:

1.業(yè)務(wù)線程-1 獲取到鎖隅茎,但未開始執(zhí)行業(yè)務(wù)澄峰。

2.業(yè)務(wù)線程-2 發(fā)生長時間的 GC。

3.業(yè)務(wù)線程-1 和 ZK 的心跳發(fā)生斷鏈辟犀。

4.lock0001 的臨時節(jié)點因為心跳斷鏈而被刪除俏竞。

5.業(yè)務(wù)線程-2 獲取到鎖。

6.業(yè)務(wù)線程-2 開始執(zhí)行業(yè)務(wù)堂竟。

7.業(yè)務(wù)線程-1 GC完畢魂毁,開始執(zhí)行業(yè)務(wù)。

8.業(yè)務(wù)線程-1 和業(yè)務(wù)線程-2 同時執(zhí)行業(yè)務(wù)出嘹。

基于 Etcd 分布式鎖

Etcd 分布式鎖的實現(xiàn)原理

Etcd 實現(xiàn)分布式鎖比 ZK 要簡單很多席楚,就是使用 Key Value 的方式進行寫入。

在集群中税稼,如果存在 Key 的話就不能寫入烦秩,也就意味著不能獲取到鎖,如果集群中郎仆,可以寫入 Key只祠,就意味著獲取得到鎖。

Etcd 到使用了 Raft 保證了集群的一致性扰肌,也就是在外界看來抛寝,只要 Etcd 集群中某一臺機器存在了鎖,所有的機器也就存在了鎖狡耻。

這個跟 ZK 一樣屬于強一致性,并且數(shù)據(jù)是可以進行持久化猴凹,默認數(shù)據(jù)一更新就持久化夷狰。

鎖的租期續(xù)約問題

Etcd 并不存在一個心跳的機制,所以跟 Redis 一樣獲取鎖的時候就要對其進行 Expire 的指定郊霎,這個時候就存在一個鎖的租期問題沼头。

租期問題有幾種思路可以去解決,這里討論其中一種:在獲取到鎖的業(yè)務(wù)線程书劝,可以開啟一個子線程去維護和輪訓(xùn)這把鎖的有效時間进倍,并定時的對這把鎖進行續(xù)租。

假設(shè)業(yè)務(wù)線程獲取到一把鎖购对,鎖的 Expire 時間為 10s猾昆,業(yè)務(wù)線程會開啟一個子線程通過輪訓(xùn)的方式每 2 秒鐘去把這把鎖進行續(xù)租,每次都將鎖的 Expire 還原到 10s骡苞。

當(dāng)業(yè)務(wù)線程執(zhí)行完業(yè)務(wù)時垂蜗,會把這把鎖進行刪除楷扬,事件完畢。

這種思路一樣會存在問題:

  • 客戶端掛掉贴见,業(yè)務(wù)線程和續(xù)租子線程都會掛掉烘苹,鎖最終會釋放。

  • 業(yè)務(wù)線程假死片部,這個跟 ZK 的假死情況一樣镣衡,也是屬于業(yè)務(wù)代碼應(yīng)該解決的問題。

  • 客戶端超長 GC 問題档悠,長 GC 導(dǎo)致續(xù)租子進程沒有進行及時續(xù)租廊鸥,鎖被超時釋放。(GC 的問題可能是個極端問題站粟,一般 GC 超過幾秒就可能去查看問題了)

總結(jié)

首先得了解清楚我們使用分布式鎖的場景黍图,為何使用分布式鎖,用它來幫我們解決什么問題奴烙,先聊場景后聊分布式鎖的技術(shù)選型助被。

無論是 Redis,ZK切诀,Etcd揩环,其實在各個場景下或多或少都存在一些問題,例如:

  • Redis 的 AP 模型會限制很多使用場景幅虑,但它卻擁有了幾者中最高的性能丰滑。

  • ZK 的分布式鎖要比 Redis 可靠很多,但他繁瑣的實現(xiàn)機制導(dǎo)致了它的性能不如 Redis倒庵,而且 ZK 會隨著集群的擴大而性能更加下降褒墨。

  • Etcd 看似是一種折中的方案,不過像鎖的租期續(xù)約都要自己去實現(xiàn)擎宝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末郁妈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绍申,更是在濱河造成了極大的恐慌噩咪,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件极阅,死亡現(xiàn)場離奇詭異胃碾,居然都是意外死亡,警方通過查閱死者的電腦和手機筋搏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門仆百,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奔脐,你說我怎么就攤上這事儒旬±刚耍” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵栈源,是天一觀的道長挡爵。 經(jīng)常有香客問我,道長甚垦,這世上最難降的妖魔是什么茶鹃? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮艰亮,結(jié)果婚禮上闭翩,老公的妹妹穿的比我還像新娘。我一直安慰自己迄埃,他們只是感情好疗韵,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侄非,像睡著了一般蕉汪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逞怨,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天者疤,我揣著相機與錄音,去河邊找鬼叠赦。 笑死驹马,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的除秀。 我是一名探鬼主播糯累,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼册踩!你這毒婦竟也來了泳姐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棍好,失蹤者是張志新(化名)和其女友劉穎仗岸,沒想到半個月后允耿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體借笙,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年较锡,在試婚紗的時候發(fā)現(xiàn)自己被綠了业稼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚂蕴,死狀恐怖低散,靈堂內(nèi)的尸體忽然破棺而出俯邓,到底是詐尸還是另有隱情,我是刑警寧澤熔号,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布稽鞭,位于F島的核電站,受9級特大地震影響引镊,放射性物質(zhì)發(fā)生泄漏朦蕴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一弟头、第九天 我趴在偏房一處隱蔽的房頂上張望吩抓。 院中可真熱鬧,春花似錦赴恨、人聲如沸疹娶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雨饺。三九已至,卻和暖如春除师,著一層夾襖步出監(jiān)牢的瞬間沛膳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工汛聚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锹安,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓倚舀,卻偏偏與公主長得像叹哭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子痕貌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容