注:轉(zhuǎn)載請注明出處:http://www.reibang.com/p/d93a4f98067e
分布式鎖的實(shí)現(xiàn)原理也是面試的一大考點(diǎn)享郊,現(xiàn)就其進(jìn)行總結(jié)如下:
1:為什么需要分布式鎖仪芒?
首先我們應(yīng)該先了解一下分布式鎖的使用場景泉手,然后再來理解為什么需要分布式鎖∶淄現(xiàn)我舉兩個例子來進(jìn)行闡述:
應(yīng)用場景:
1:銀行轉(zhuǎn)賬問題(該場景不太好解釋):A在上海宙项,B在北京同時在建行轉(zhuǎn)賬給杭州C鲸阻,A轉(zhuǎn)賬時切诀,會修改C處服務(wù)器的表诬留,B不能在此刻轉(zhuǎn)賬斜纪,同理,B轉(zhuǎn)賬時文兑,A不能做處理盒刚,A,B的轉(zhuǎn)賬操作時同步绿贞,必須保證數(shù)據(jù)的一致性因块,這就需要分布式鎖來進(jìn)行處理。
2:取任務(wù)問題:某服務(wù)提供一組任務(wù)籍铁,A系統(tǒng)請求隨機(jī)從任務(wù)組中獲取一個任務(wù)涡上;B系統(tǒng)請求隨機(jī)從任務(wù)組中獲取一個任務(wù)。 在理想的情況下寨辩,A從任務(wù)組中挑選一個任務(wù)吓懈,任務(wù)組刪除該任務(wù),B從剩下的的任務(wù)中再挑一個靡狞,任務(wù)組刪除該任務(wù)耻警。 同樣的,在真實(shí)情況下甸怕,如果不做任何處理甘穿,可能會出現(xiàn)A和B挑中了同一個任務(wù)的情況。
2:為什么分布式系統(tǒng)中不能用普通鎖呢梢杭?那么普通鎖和分布式鎖有什么區(qū)別呢温兼?
普通鎖:單一系統(tǒng)中,同一個應(yīng)用程序是有同一個進(jìn)程武契,然后多個線程并發(fā)會造成數(shù)據(jù)安全問題募判,他們是共享同一塊內(nèi)存的荡含,所以在內(nèi)存某個地方做標(biāo)記即可滿足需求,例如synchronized和volatile+cas一樣對具體的代碼做標(biāo)記届垫,對應(yīng)的就是在同一塊內(nèi)存區(qū)域作了同步的標(biāo)記释液。
分布式鎖:分布式系統(tǒng)中,最大的區(qū)別就是不同系統(tǒng)中的應(yīng)用程序都是在各自機(jī)器上不同的進(jìn)程中處理的装处,這里的線程不安全可以理解為多進(jìn)程造成的數(shù)據(jù)安全問題误债,他們不會共享同一臺機(jī)器的同一塊內(nèi)存區(qū)域,因此需要將標(biāo)記存儲在所有進(jìn)程都能看到的地方妄迁。例如zookeeper作分布式鎖寝蹈,就是將鎖標(biāo)記存儲在多個進(jìn)程共同看到的地方,redis作分布式鎖登淘,是將其標(biāo)記公共內(nèi)存箫老,而不是某個進(jìn)程分配的區(qū)域。
3:分布式鎖的三種實(shí)現(xiàn)方式
注:用何種分布式鎖以公司為主黔州。
a:zookeeper實(shí)現(xiàn)分布式鎖(用的最多)
實(shí)現(xiàn)方式:
方案1:利用節(jié)點(diǎn)名稱的唯一性來實(shí)現(xiàn)共享鎖槽惫。
算法思路: 利用名稱唯一性,加鎖操作時辩撑,只需要所有客戶端一起創(chuàng)建/test/Lock節(jié)點(diǎn),只有一個創(chuàng)建成功仿耽,成功者獲得鎖合冀。解鎖時,只需刪除/test/Lock節(jié)點(diǎn)项贺,其余客戶端再次進(jìn)入競爭創(chuàng)建節(jié)點(diǎn)君躺,直到所有客戶端都獲得鎖。
方案2:利用臨時順序節(jié)點(diǎn)實(shí)現(xiàn)共享鎖开缎。(主要是用這種方式實(shí)現(xiàn))
算法思路:對于加鎖操作棕叫,可以讓所有客戶端都去/lock目錄下創(chuàng)建臨時順序節(jié)點(diǎn),如果創(chuàng)建的客戶端發(fā)現(xiàn)自身創(chuàng)建節(jié)點(diǎn)序列號是/lock/目錄下最小的節(jié)點(diǎn)奕删,則獲得鎖俺泣。否則,監(jiān)視比自己創(chuàng)建節(jié)點(diǎn)的序列號小的節(jié)點(diǎn)(比自己創(chuàng)建的節(jié)點(diǎn)小的最大節(jié)點(diǎn))完残,進(jìn)入等待伏钠。
比如創(chuàng)建節(jié)點(diǎn):/lock/0000000001、/lock/0000000002谨设、/lock/0000000003熟掂。則節(jié)點(diǎn)/lock/0000000001會先獲得鎖,因?yàn)閦k上的節(jié)點(diǎn)是有序的扎拣,且都是最小的節(jié)點(diǎn)先獲得鎖赴肚。
注:臨時順序節(jié)點(diǎn)比持久順序節(jié)點(diǎn)的好處是:當(dāng)zookeeper宕機(jī)后素跺,臨時順序節(jié)點(diǎn)會自動刪除,獲取鎖的客戶端會釋放鎖誉券,不會一直造成鎖等待指厌,而持久節(jié)點(diǎn)會造成鎖等待。
兩種方式的區(qū)別:
方案1會產(chǎn)生驚群效應(yīng):假如許多客戶端在等待一把鎖横朋,當(dāng)鎖釋放時候所有客戶端都被喚醒仑乌,然后競爭分布式鎖,僅僅有一個客戶端得到鎖琴锭。
方案2是按照創(chuàng)建順序排隊(duì)的實(shí)現(xiàn)晰甚,多個客戶端共同等待鎖,當(dāng)鎖釋放時只有一個客戶端會被喚醒决帖,在zk上注冊節(jié)點(diǎn)最小的客戶端會被喚醒厕九,避免了驚群效應(yīng)。
b:redis實(shí)現(xiàn)分布式鎖(用的次之)
redis實(shí)現(xiàn)分布式鎖主要靠四個命令:
setnx(set if not exits 維護(hù)著是樂觀鎖):當(dāng)不存在key的時候地回,才為key設(shè)置值為value扁远。setnx與set的區(qū)別:set是存在key,則去覆蓋value刻像;setnx是不存在key畅买,則重新給key和value賦值。
getset:根據(jù)key得到舊的值细睡,并set新的值谷羞。
expire:設(shè)置過期時間。
del:刪除
實(shí)現(xiàn)方式(文字配合圖片可更加清晰):
1:獲取鎖的時候溜徙,使用setnx加鎖湃缎,并使用expire命令為鎖添加一個超時時間,超過該時間則自動釋放鎖蠢壹,鎖的value值為一個隨機(jī)生成的UUID嗓违,通過此在釋放鎖的時候進(jìn)行判斷。
2:獲取鎖的時候還設(shè)置一個獲取的超時時間图贸,若超過這個時間則放棄獲取鎖蹂季。
3:釋放鎖的時候,通過UUID判斷是不是該鎖求妹,若是該鎖乏盐,則執(zhí)行delete進(jìn)行鎖釋放。
c:數(shù)據(jù)庫實(shí)現(xiàn)分布式鎖(用的最少)
實(shí)現(xiàn)方式:利用的是樂觀鎖和悲觀鎖
樂觀鎖:在表中添加版本號的字段制恍,每次更新前都先查詢出帶版本號的數(shù)據(jù)父能,然后再更新的時候where條件語句后帶版本號條件,更新成功表示鎖已占用净神,更新不成功表示鎖沒被占用何吝。
悲觀鎖:利用select...for update(X鎖)/select...lock in share mode(S鎖)溉委,一般來說用X鎖的較多,因?yàn)楹罄m(xù)多會做寫功能的實(shí)現(xiàn)爱榕。
注:當(dāng)實(shí)現(xiàn)悲觀鎖的時候瓣喊,需要關(guān)閉數(shù)據(jù)庫的事務(wù)自動提交機(jī)制不然不會生效。因此java代碼中應(yīng)該選擇主動關(guān)閉數(shù)據(jù)庫的事務(wù)自動提交功能黔酥。