解析分布式鎖之Redis實現(xiàn)(二)

摘要:在前文中提及了實現(xiàn)分布式鎖目前有三種流行方案州袒,分別為基于數(shù)據(jù)庫、Redis弓候、Zookeeper的方案郎哭,本文主要闡述基于Redis的分布式鎖,分布式架構(gòu)設(shè)計如今在企業(yè)中被大量的應(yīng)用菇存,而在不同的分布式節(jié)點進行協(xié)同工作的時候夸研,節(jié)點服務(wù)的時序、結(jié)果的正確性以及執(zhí)行成本也成為了必須考慮的重要因素依鸥。其中競態(tài)條件會導(dǎo)致執(zhí)行結(jié)果的不正確亥至,不同服務(wù)節(jié)點同時處理同一任務(wù)也將耗費不必需的系統(tǒng)資源,如果解決呢贱迟?方式之一可以選擇分布式鎖姐扮,本文介紹如果通過redis實現(xiàn)分布式鎖,也歡迎大家和我一起討論衣吠。

分布式鎖的基本應(yīng)用場景和設(shè)計原則

我們先來看一個簡單的案例:有三個服務(wù)茶敏,一個是訂單服務(wù)orderService,一個是報表服務(wù)(reportService)缚俏,一個是推送服務(wù)(pushService)惊搏,每個服務(wù)都橫向部署在2個節(jié)點上。報表服務(wù)每天凌晨12點需要從訂單服務(wù)拉取訂單數(shù)據(jù)并生成報表忧换,并且在每天早上8點通過推送服務(wù)向用戶發(fā)送新生成的數(shù)據(jù)報表恬惯,需要如何設(shè)計這個流程?

首先我們需要了解該流程的兩個關(guān)鍵點亚茬,第一酪耳,報表服務(wù)的2個節(jié)點只能有一個節(jié)點生成報表,否則會浪費系統(tǒng)資源刹缝,該關(guān)鍵點沒有高可靠的要求(重復(fù)覆蓋生成并不會得到錯誤結(jié)果)葡兑;第二,向同一個用戶推送該數(shù)據(jù)報表也只能有一個節(jié)點去執(zhí)行赞草,否則用戶會收到兩份一樣的報表讹堤,該關(guān)鍵點有高可靠要求。

我們可以從兩個關(guān)鍵點中提取一個相同點厨疙,必須要設(shè)置一把鎖洲守,獲的該鎖的節(jié)點才能執(zhí)行指定的任務(wù)。同時還能提取到一個不同點,那就是兩種場景對獲取鎖的依賴程度不一致梗醇。我們來對該流程進行簡單建模:

通過上圖的流程已經(jīng)可以實現(xiàn)簡單可靠的鎖機制知允,當(dāng)然這是有前提的。

首先鎖服務(wù)必須足夠穩(wěn)定叙谨,假設(shè)無法獲取鎖温鸽,那么競爭任務(wù)的將無法執(zhí)行。其次手负,執(zhí)行競爭任務(wù)的過程不能夠死鎖或者無限等待涤垫,否則將無法釋放鎖且改任務(wù)也無法執(zhí)行完成。所以在設(shè)計鎖的時候還需要考慮兩個因素:鎖必須要有過期時間及獲取及釋放鎖過程的高可用或者鎖錯誤時的異常處理竟终。

所以蝠猬,歸納一下分布式鎖在設(shè)計時通常要考慮的幾個要素是:

分布式鎖一定要保證多客戶端競爭臨界資源時的絕對互斥;

分布式鎖要設(shè)計一定的超時時間统捶,防止在獲得鎖的服務(wù)阻塞或者崩潰引起的鎖無法釋放榆芦;

分布式要針對業(yè)務(wù)場景設(shè)計鎖機制異常降級措施,防止因為鎖獲取錯誤導(dǎo)致無法獲取臨界資源的后果喘鸟。

關(guān)于第2點的要素匆绣,還有一些要注意的東西,假設(shè)報表服務(wù)A在獲取到鎖之后什黑,出現(xiàn)了很長的FULL GC崎淳,系統(tǒng)出現(xiàn)暫停,在此期間兑凿,鎖已經(jīng)超時了凯力,報表服務(wù)B又重新拿到了鎖并向用戶發(fā)送了報表茵瘾,在客戶端AFull GC結(jié)束后礼华,同樣再去執(zhí)行報表發(fā)送任務(wù),就會導(dǎo)致執(zhí)行結(jié)果出錯拗秘。

這種場景往往需要個性化的處理圣絮,現(xiàn)在業(yè)界大部分的分布式鎖都會出現(xiàn)這種情況,因為系統(tǒng)暫停導(dǎo)致的鎖失效往往很難去避免雕旨,因為系統(tǒng)暫桶缃常可能出現(xiàn)在任何時候。 通常情況下凡涩,我們需要預(yù)估訪問競爭資源的時間棒搜,確定好超時時間并在訪問結(jié)束后進行數(shù)據(jù)比對和必要的數(shù)據(jù)補償。

Redis具體實現(xiàn)分布式鎖

在redis命令集合中活箕,有一個命令叫做SETNX力麸,具體命令格式是:SETNX key value

該命令的作用是如果key存在,則什么都不做,并且返回0克蚂,如果key不存在則將key的值設(shè)置成value闺鲸,并且返回1,該命令是原子性的埃叭。我們可以利用該命令來實現(xiàn)分布式鎖摸恍。

獲取鎖:獲取當(dāng)前的timestamp,并將客戶端ID作為key赤屋,該timestamp作為value調(diào)用SETNX立镶,并設(shè)置鎖的TTL,處理獲取鎖的異常益缎。

確認(rèn)鎖狀態(tài)谜慌,如果成功獲取鎖,則訪問臨界資源莺奔,否則根據(jù)業(yè)務(wù)場景間隔一定時間再次嘗試獲取鎖欣范。

訪問臨界資源

釋放鎖

//獲取鎖

timeStamp?=?getCurrentTimeStamp();

try{

? ?lock=SET CLIENT_ID timeStamp NX PX TIMEOUT;

}catch(Exception?e){

? ?//處理獲取鎖的異常

? ?return;

}

try{

? ?if(lock?==?0){

? ? ? ?return;

? ?}else{

? ? ? ?//訪問臨界資源

? ? ? ?do();

? ?}

}finally{

? ?//釋放鎖

? ?del?CLIENT_ID;

}

這種實現(xiàn)分布式鎖的方式是很多開發(fā)者最喜歡用的,但是如何保證redis的可用性呢令哟,如果我們使用一個redis節(jié)點恼琼,當(dāng)其因為不可控原因宕機時,鎖機制將不可用屏富。有人可能會說晴竞,可以使用redis主從集群復(fù)制,主掛了狠半,從可以接替上噩死,但是這估計依然不能解決問題,因為redis主從復(fù)制是異步的神年,誰能保證主掛了窍奋,從節(jié)點上一定有鎖數(shù)據(jù)呢考榨?

redis官網(wǎng)上介紹了一種red lock算法悼泌,該算法棄用了單redis節(jié)點瓶珊,采用N個(官網(wǎng)推薦5個)獨立的redis節(jié)點作為鎖服務(wù),客戶端要獲取鎖飘千,必須向N/2+1(絕大部分)節(jié)點成功申請鎖后堂鲜,才能訪問臨界資源。

但是該算法中獲取鎖的過程變的復(fù)雜了护奈,時間也就越不可控缔莲,假設(shè)從redis1節(jié)點獲取鎖成功開始到從redis(N/2+1)獲取鎖成功結(jié)束到時間為SPACETIME,鎖到有效時間不再是key到TTL霉旗,而是:

REMAIN_TIME=TTL-SPACETIME

當(dāng)SPACETIME比較大時痴奏,客戶端非常有可能獲取到一個已經(jīng)失效到鎖磺箕,所以在獲取鎖之后red lock算法需要再次驗證鎖是否失效。

//獲取鎖

timeStamp?=?getCurrentTimeStamp();

//向N/2+1個節(jié)點申請鎖

int?successLockNum=0;

boolean?lockSuccess=false;

for(int?i=1;i<5;i++){

? ?try{

? ? ? ?lock=SET CLIENT_ID timeStamp NX PX TIMEOUT;

? ? ? ?if(lock?==?1?&&?++successLockNum?==?N/2+1){

lockSuccess?=?true;

? ? ? ? ? ?break;

? ? ? ?}

? ?}catch(Exception?e){

? ? ? ?//處理獲取鎖的異常

? ? ? ?return;

? ?}

}

//驗證獲取鎖是否成功

if(!successLockNum){

? ?//獲取鎖失敗

? ?return;

}

//驗證獲取到到鎖是否是無效鎖

nowTimeStamp?=?getCurrentTimeStamp();

if(nowTimeStamp-timeStamp>TTL){

? ?//無效鎖

? ?return;

}

try{

? ?//訪問臨界資源

? ?do();

}finally{

? ?//釋放鎖

? ?del?CLIENT_ID;

}

后續(xù)

用Redis來實現(xiàn)分布式鎖機制在業(yè)界非常常用抛虫,但是我們在應(yīng)用過程中一定要注意實現(xiàn)鎖到超時避免死鎖以及因為服務(wù)暫停導(dǎo)致鎖失效到情況松靡,每種情況到解決方案需要個性化到去解決。Red lock算法在一定程度上解決了分布式鎖服務(wù)的穩(wěn)定性問題建椰,但是帶來了系統(tǒng)復(fù)雜度雕欺,同時也有人在質(zhì)疑了該算法,有興趣到可以在搜索引擎搜索棉姐。本文就到這里屠列,如有錯誤,歡迎指正伞矩。

想要了解更多分布式知識點的笛洛,可以加群:?537775426(備注好信息),我會把關(guān)于分布式的知識點放在群的共享區(qū)里面乃坤,我也會在群里面分享我從業(yè)多年的一些工作經(jīng)驗苛让,希望我的工作經(jīng)驗可以幫助大家在成為架構(gòu)師的道路上面少走彎路。帶著大家全面湿诊、科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知狱杰!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市厅须,隨后出現(xiàn)的幾起案子仿畸,更是在濱河造成了極大的恐慌,老刑警劉巖朗和,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件错沽,死亡現(xiàn)場離奇詭異,居然都是意外死亡眶拉,警方通過查閱死者的電腦和手機千埃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镀层,“玉大人镰禾,你說我怎么就攤上這事皿曲〕辏” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵屋休,是天一觀的道長坞古。 經(jīng)常有香客問我,道長劫樟,這世上最難降的妖魔是什么痪枫? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任织堂,我火速辦了婚禮,結(jié)果婚禮上奶陈,老公的妹妹穿的比我還像新娘易阳。我一直安慰自己,他們只是感情好吃粒,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布潦俺。 她就那樣靜靜地躺著,像睡著了一般徐勃。 火紅的嫁衣襯著肌膚如雪事示。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天僻肖,我揣著相機與錄音肖爵,去河邊找鬼。 笑死臀脏,一個胖子當(dāng)著我的面吹牛劝堪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播揉稚,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼幅聘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了窃植?” 一聲冷哼從身側(cè)響起帝蒿,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巷怜,沒想到半個月后葛超,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡延塑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年绣张,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片关带。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡侥涵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宋雏,到底是詐尸還是另有隱情芜飘,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布磨总,位于F島的核電站嗦明,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蚪燕。R本人自食惡果不足惜娶牌,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一奔浅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诗良,春花似錦汹桦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至壹罚,卻和暖如春葛作,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背猖凛。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工赂蠢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辨泳。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓虱岂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親菠红。 傳聞我的和親對象是個殘疾皇子第岖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

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