Java分布式鎖,搞懂分布式鎖實(shí)現(xiàn)看這篇文章就對(duì)了

前言

隨著微處理機(jī)技術(shù)的發(fā)展酝润,人們只需花幾百美元就能買到一個(gè)CPU芯片肿孵,這個(gè)芯片每秒鐘執(zhí)行的指令比80年代最大的大型機(jī)的處理機(jī)每秒鐘所執(zhí)行的指令還多碴犬。如果你愿意付出兩倍的價(jià)錢,將得到同樣的CPU芳来,但它卻以更高的時(shí)鐘速率運(yùn)行。因此猜拾,最節(jié)約成本的辦法通常是在一個(gè)系統(tǒng)中使用集中在一起的大量的廉價(jià)CPU即舌。所以,傾向于分布式系統(tǒng)的主要原因是它可以潛在地得到比單個(gè)的大型集中式系統(tǒng)好得多的性價(jià)比关带。實(shí)際上侥涵,分布式系統(tǒng)是通過較低廉的價(jià)格來實(shí)現(xiàn)相似的性能的。

隨著互聯(lián)網(wǎng)的興起宋雏,越來越多的人使用者互聯(lián)網(wǎng)產(chǎn)品芜飘。一般互聯(lián)網(wǎng)系統(tǒng)都是分布式部署的,分布式部署確實(shí)能帶來性能和效率上的提升磨总,提升效率的同事嗦明,我們還需要注意,保證一個(gè)分布式環(huán)境下數(shù)據(jù)一致性的問題蚪燕。

分布式鎖簡(jiǎn)述

在單機(jī)時(shí)代娶牌,雖然不存在分布式鎖,但也會(huì)面臨資源互斥的情況馆纳,只不過在單機(jī)的情況下诗良,如果有多個(gè)線程要同時(shí)訪問某個(gè)共享資源的時(shí)候,我們可以采用線程間加鎖的機(jī)制鲁驶,即當(dāng)某個(gè)線程獲取到這個(gè)資源后鉴裹,就需要對(duì)這個(gè)資源進(jìn)行加鎖,當(dāng)使用完資源之后钥弯,再解鎖径荔,其它線程就可以接著使用了。例如脆霎,在JAVA中总处,甚至專門提供了一些處理鎖機(jī)制的一些API(synchronize/Lock等)。

但是到了分布式系統(tǒng)的時(shí)代睛蛛,這種線程之間的鎖機(jī)制鹦马,就沒作用了,系統(tǒng)可能會(huì)有多份并且部署在不同的機(jī)器上忆肾,這些資源已經(jīng)不是在線程之間共享了菠红,而是屬于進(jìn)程之間共享的資源。因此难菌,為了解決這個(gè)問題,「分布式鎖」就強(qiáng)勢(shì)登場(chǎng)了蔑滓。

分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式郊酒。在分布式系統(tǒng)中遇绞,常常需要協(xié)調(diào)他們的動(dòng)作。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源燎窘,那么訪問這些資源的時(shí)候摹闽,往往需要互斥來防止彼此干擾來保證一致性,在這種情況下褐健,便需要使用到分布式鎖付鹿。

在分布式系統(tǒng)中,常常需要協(xié)調(diào)他們的動(dòng)作蚜迅。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源舵匾,那么訪問這些資源的時(shí)候,往往需要互斥來防止彼此干擾來保證一致性谁不,這個(gè)時(shí)候坐梯,便需要使用到分布式鎖。

分布式鎖要滿足哪些要求呢刹帕?

排他性:在同一時(shí)間只會(huì)有一個(gè)客戶端能獲取到鎖吵血,其它客戶端無法同時(shí)獲取

避免死鎖:這把鎖在一段有限的時(shí)間之后,一定會(huì)被釋放(正常釋放或異常釋放)

高可用:獲取或釋放鎖的機(jī)制必須高可用且性能佳

目前相對(duì)主流的有三種偷溺,從實(shí)現(xiàn)的復(fù)雜度上來看蹋辅,從上往下難度依次增加:

數(shù)據(jù)庫(MySQL)

Redis

ZooKeeper

基于數(shù)據(jù)庫實(shí)現(xiàn)

基于數(shù)據(jù)庫來做分布式鎖的話,通常有兩種做法:

1.基于數(shù)據(jù)庫的樂觀鎖

2.基于數(shù)據(jù)庫的悲觀鎖

樂觀鎖

樂觀鎖的特點(diǎn)先進(jìn)行業(yè)務(wù)操作挫掏,不到萬不得已不去拿鎖侦另。即“樂觀”的認(rèn)為拿鎖多半是會(huì)成功的,因此在進(jìn)行完業(yè)務(wù)操作需要實(shí)際更新數(shù)據(jù)的最后一步再去拿一下鎖就好砍濒。

樂觀鎖機(jī)制其實(shí)就是在數(shù)據(jù)庫表中引入一個(gè)版本號(hào)(version)字段來實(shí)現(xiàn)的淋肾。當(dāng)我們要從數(shù)據(jù)庫中讀取數(shù)據(jù)的時(shí)候,同時(shí)把這個(gè)version字段也讀出來爸邢,如果要對(duì)讀出來的數(shù)據(jù)進(jìn)行更新后寫回?cái)?shù)據(jù)庫樊卓,則需要將version加1,同時(shí)將新的數(shù)據(jù)與新的version更新到數(shù)據(jù)表中杠河,且必須在更新的時(shí)候同時(shí)檢查目前數(shù)據(jù)庫里version值是不是之前的那個(gè)version碌尔,如果是,則正常更新券敌。如果不是唾戚,則更新失敗,說明在這個(gè)過程中有其它的進(jìn)程去更新過數(shù)據(jù)了待诅。

看圖敘事叹坦。模擬實(shí)戰(zhàn)場(chǎng)景。


如上圖卑雁,故事男主人公(以下簡(jiǎn)稱男主)打算去ATM機(jī)取3000元募书,故事女主人公(以下簡(jiǎn)稱女主)則要在某寶買買買绪囱,買個(gè)包需要3000元,賬戶的余額是5000元莹捡。如果沒有采用鎖的話鬼吵,在兩人同時(shí)取款和買買買,可能會(huì)出現(xiàn)合計(jì)消費(fèi)了6000篮赢,導(dǎo)致賬戶余額異常齿椅。所以需要用到鎖的機(jī)制,當(dāng)男主女主甚至更多小主同時(shí)消費(fèi)時(shí)启泣,除了讀取到6000的賬戶余額外涣脚,還需要讀取到當(dāng)前的版本號(hào)version=1,等先行消費(fèi)成功的主人公(無論誰先消費(fèi))去出發(fā)修改賬戶余額的同時(shí)种远,會(huì)觸發(fā)version=version+1涩澡,即version=2。那么其他人使用未更新的version(1)去更新賬戶余額時(shí)就會(huì)發(fā)現(xiàn)版本號(hào)不對(duì)坠敷,就會(huì)導(dǎo)致本次更新失敗妙同,就得重新去讀取最新賬戶余額以及版本號(hào)。

樂觀鎖遵循的兩點(diǎn)法則:

1.鎖服務(wù)要有遞增的版本號(hào)version

2.每次更新數(shù)據(jù)的時(shí)候都必須先判斷版本號(hào)對(duì)不對(duì)膝迎,然后再寫入新的版本號(hào)

悲觀鎖

悲觀鎖的特點(diǎn)是先獲取鎖粥帚,再進(jìn)行業(yè)務(wù)操作,即“悲觀”的認(rèn)為獲取鎖是非常有可能失敗的限次,因此要先確保獲取鎖成功再進(jìn)行業(yè)務(wù)操作芒涡。

通常所說的“一鎖二查三更新”即指的是使用悲觀鎖。通常來講在數(shù)據(jù)庫上的悲觀鎖需要數(shù)據(jù)庫本身提供支持卖漫,即通過常用的select … for update操作來實(shí)現(xiàn)悲觀鎖费尽。當(dāng)數(shù)據(jù)庫執(zhí)行select for update時(shí)會(huì)獲取被select中的數(shù)據(jù)行的行鎖,因此其他并發(fā)執(zhí)行的select for update如果試圖選中同一行則會(huì)發(fā)生排斥(需要等待行鎖被釋放)羊始,因此達(dá)到鎖的效果旱幼。select for update獲取的行鎖會(huì)在當(dāng)前事務(wù)結(jié)束時(shí)自動(dòng)釋放,因此必須在事務(wù)中使用突委。

示例:


樂觀鎖與悲觀鎖的區(qū)別

樂觀鎖的思路一般是表中增加版本字段柏卤,更新時(shí)where語句中增加版本的判斷,算是一種CAS(Compare And Swep)操作匀油,銀行消費(fèi)場(chǎng)景中version起到了版本控制的作用( AND version=#{version})缘缚。

悲觀鎖之所以是悲觀,在于他認(rèn)為本次操作會(huì)發(fā)生并發(fā)沖突敌蚜,所以一開始就對(duì)銀行賬戶加上鎖(SELECT … FOR UPDATE)桥滨,然后就可以安心的做判斷和更新,因?yàn)檫@時(shí)候不會(huì)有別人更新賬戶余額。

基于Redis實(shí)現(xiàn)

基于Redis實(shí)現(xiàn)的鎖機(jī)制该园,主要是依賴redis自身的原子操作酸舍,例如:

SET user_key user_value NX PX 100

redis從2.6.12版本開始,SET命令才支持這些參數(shù):

NX:只在在鍵不存在時(shí)里初,才對(duì)鍵進(jìn)行設(shè)置操作,SET key value NX 效果等同于 SETNX key value

PX millisecond:設(shè)置鍵的過期時(shí)間為millisecond毫秒忽舟,當(dāng)超過這個(gè)時(shí)間后双妨,設(shè)置的鍵會(huì)自動(dòng)失效

上述代碼示例是指,當(dāng)redis中不存在user_key這個(gè)鍵的時(shí)候叮阅,才會(huì)去設(shè)置一個(gè)user_key鍵刁品,并且給這個(gè)鍵的值設(shè)置為 user_value,且這個(gè)鍵的存活時(shí)間為100ms

為什么這個(gè)命令可以幫我們實(shí)現(xiàn)鎖機(jī)制呢浩姥?

因?yàn)檫@個(gè)命令是只有在某個(gè)key不存在的時(shí)候挑随,才會(huì)執(zhí)行成功。那么當(dāng)多個(gè)進(jìn)程同時(shí)并發(fā)的去設(shè)置同一個(gè)key的時(shí)候勒叠,就永遠(yuǎn)只會(huì)有一個(gè)進(jìn)程成功兜挨。當(dāng)某個(gè)進(jìn)程設(shè)置成功之后,就可以去執(zhí)行業(yè)務(wù)邏輯了眯分,等業(yè)務(wù)邏輯執(zhí)行完畢之后拌汇,再去進(jìn)行解鎖。

解鎖很簡(jiǎn)單弊决,只需要?jiǎng)h除這個(gè)key就可以了噪舀,不過刪除之前需要判斷,這個(gè)key對(duì)應(yīng)的value是當(dāng)初自己設(shè)置的那個(gè)飘诗。

另外与倡,針對(duì)redis集群模式的分布式鎖,可以采用redis的Redlock(可能會(huì)被墻)機(jī)制昆稿。

基于ZooKeeper實(shí)現(xiàn)

其實(shí)基于ZooKeeper纺座,就是使用它的臨時(shí)有序節(jié)點(diǎn)來實(shí)現(xiàn)的分布式鎖。

原理

當(dāng)某客戶端要進(jìn)行邏輯的加鎖時(shí)貌嫡,就在zookeeper上的某個(gè)指定節(jié)點(diǎn)的目錄下比驻,去生成一個(gè)唯一的臨時(shí)有序節(jié)點(diǎn), 然后判斷自己是否是這些有序節(jié)點(diǎn)中序號(hào)最小的一個(gè)岛抄,如果是别惦,則算是獲取了鎖。如果不是夫椭,則說明沒有獲取到鎖掸掸,那么就需要在序列中找到比自己小的那個(gè)節(jié)點(diǎn),并對(duì)其調(diào)用exist()方法,對(duì)其注冊(cè)事件監(jiān)聽扰付,當(dāng)監(jiān)聽到這個(gè)節(jié)點(diǎn)被刪除了堤撵,那就再去判斷一次自己當(dāng)初創(chuàng)建的節(jié)點(diǎn)是否變成了序列中最小的。如果是羽莺,則獲取鎖实昨,如果不是,則重復(fù)上述步驟盐固。

當(dāng)釋放鎖的時(shí)候荒给,只需將這個(gè)臨時(shí)節(jié)點(diǎn)刪除即可。


如上圖刁卜,locker是一個(gè)持久節(jié)點(diǎn)志电,node_1/node_2/…/node_n 就是上面說的臨時(shí)節(jié)點(diǎn),由客戶端client去創(chuàng)建的蛔趴。

client_1/client_2/…/clien_n 都是想去獲取鎖的客戶端挑辆。以client_1為例,它想去獲取分布式鎖孝情,則需要跑到locker下面去創(chuàng)建臨時(shí)節(jié)點(diǎn)(假如是node_1)創(chuàng)建完畢后鱼蝉,看一下自己的節(jié)點(diǎn)序號(hào)是否是locker下面最小的织咧,如果是伐割,則獲取了鎖蜀细。如果不是绍弟,則去找到比自己小的那個(gè)節(jié)點(diǎn)(假如是node_2)看靠,找到后坚弱,就監(jiān)聽node_2稍味,直到node_2被刪除隧熙,那么就開始再次判斷自己的node_1是不是序列中最小的婉弹,如果是睬魂,則獲取鎖,如果還不是镀赌,則繼續(xù)找一下一個(gè)節(jié)點(diǎn)氯哮。

博客總結(jié)

分布式鎖有很多種,開篇說的"相對(duì)主流的有三種"只是針對(duì)我所遇到的商佛。分布式鎖未來肯定是千變?nèi)f化的喉钢,無論你身處一個(gè)什么樣的公司,最開始的工作可能都得盡可能的從簡(jiǎn)單的做起良姆。希望大家能根據(jù)所在公司業(yè)務(wù)場(chǎng)景肠虽,選擇適合所在項(xiàng)目的方案。

如果大家想學(xué)習(xí)以下路線內(nèi)容玛追,在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流群税课。交流學(xué)習(xí)群號(hào):874811168 里面會(huì)分享一些資深架構(gòu)師錄制的視頻錄像:有Spring闲延,MyBatis,Netty源碼分析韩玩,高并發(fā)垒玲、高性能、分布式找颓、微服務(wù)架構(gòu)的原理合愈,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識(shí)體系叮雳。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源想暗,目前受益良多

其他參考資料:


出  處:http://www.cnblogs.com/toutou/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市帘不,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杨箭,老刑警劉巖寞焙,帶你破解...
    沈念sama閱讀 212,332評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異互婿,居然都是意外死亡捣郊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,508評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門慈参,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呛牲,“玉大人,你說我怎么就攤上這事驮配∧锢” “怎么了?”我有些...
    開封第一講書人閱讀 157,812評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵壮锻,是天一觀的道長(zhǎng)琐旁。 經(jīng)常有香客問我,道長(zhǎng)猜绣,這世上最難降的妖魔是什么灰殴? 我笑而不...
    開封第一講書人閱讀 56,607評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮掰邢,結(jié)果婚禮上牺陶,老公的妹妹穿的比我還像新娘。我一直安慰自己辣之,他們只是感情好掰伸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,728評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著召烂,像睡著了一般碱工。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,919評(píng)論 1 290
  • 那天怕篷,我揣著相機(jī)與錄音历筝,去河邊找鬼。 笑死廊谓,一個(gè)胖子當(dāng)著我的面吹牛梳猪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蒸痹,決...
    沈念sama閱讀 39,071評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼春弥,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了叠荠?” 一聲冷哼從身側(cè)響起匿沛,我...
    開封第一講書人閱讀 37,802評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榛鼎,沒想到半個(gè)月后逃呼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,256評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡者娱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,576評(píng)論 2 327
  • 正文 我和宋清朗相戀三年抡笼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黄鳍。...
    茶點(diǎn)故事閱讀 38,712評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡推姻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出框沟,到底是詐尸還是另有隱情藏古,我是刑警寧澤,帶...
    沈念sama閱讀 34,389評(píng)論 4 332
  • 正文 年R本政府宣布街望,位于F島的核電站校翔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灾前。R本人自食惡果不足惜防症,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,032評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哎甲。 院中可真熱鬧蔫敲,春花似錦、人聲如沸炭玫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吞加。三九已至裙犹,卻和暖如春尽狠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叶圃。 一陣腳步聲響...
    開封第一講書人閱讀 32,026評(píng)論 1 266
  • 我被黑心中介騙來泰國打工袄膏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掺冠。 一個(gè)月前我還...
    沈念sama閱讀 46,473評(píng)論 2 360
  • 正文 我出身青樓沉馆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親德崭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斥黑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,606評(píng)論 2 350

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