日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

分布式锁的实现

發(fā)布時(shí)間:2023/12/31 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式锁的实现 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 分布式鎖的疑問(wèn)

談到分布式鎖,有很多實(shí)現(xiàn)方式,如數(shù)據(jù)庫(kù)、redis、ZooKeeper等。提個(gè)問(wèn)題:

  • 實(shí)現(xiàn)分布式鎖需要滿(mǎn)足哪些條件呢?

2 數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖

2.1 實(shí)現(xiàn)案例

如使用數(shù)據(jù)庫(kù)事務(wù)中的鎖如record lock來(lái)實(shí)現(xiàn),如下所示

1 獲取鎖

public void lock(){connection.setAutoCommit(false)int count = 0;while(count < 4){try{select * from lock where lock_name=xxx for update;if(結(jié)果不為空){//代表獲取到鎖return;}}catch(Exception e){}//為空或者拋異常的話(huà)都表示沒(méi)有獲取到鎖sleep(1000);count++;}throw new LockException(); }

2 釋放鎖

public void release(){connection.commit(); }

數(shù)據(jù)庫(kù)的lock表,lock_name是主鍵,通過(guò)for update操作,數(shù)據(jù)庫(kù)就會(huì)對(duì)該行記錄加上record lock,從而阻塞其他人對(duì)該記錄的操作。

一旦獲取到了鎖,就可以開(kāi)始執(zhí)行業(yè)務(wù)邏輯,最后通過(guò)connection.commit()操作來(lái)釋放鎖。

其他沒(méi)有獲取到鎖的就會(huì)阻塞在上述select語(yǔ)句上,可能的結(jié)果有2種,在超時(shí)之前獲取到了鎖,在超時(shí)之前仍未獲取到鎖(這時(shí)候會(huì)拋出超時(shí)異常,然后進(jìn)行重試)

數(shù)據(jù)庫(kù)當(dāng)然還有其他方式,如插入一個(gè)有唯一約束的數(shù)據(jù)。成功插入則表示獲取到了鎖,釋放鎖就是刪除該記錄。該方案也有很多問(wèn)題要解決

2.2 存在的問(wèn)題

首先性能不是特別高。

通過(guò)數(shù)據(jù)庫(kù)的鎖來(lái)實(shí)現(xiàn)多進(jìn)程之間的互斥,但是這貌似也有一個(gè)問(wèn)題:就是sql超時(shí)異常的問(wèn)題

jdbc超時(shí)具體有3種超時(shí),具體見(jiàn)深入理解JDBC的超時(shí)設(shè)置

  • 框架層的事務(wù)超時(shí)
  • jdbc的查詢(xún)超時(shí)
  • Socket的讀超時(shí)

這里只涉及到后2種的超時(shí),jdbc的查詢(xún)超時(shí)還好(mysql的jdbc驅(qū)動(dòng)會(huì)向服務(wù)器發(fā)送kill query命令來(lái)取消查詢(xún)),如果一旦出現(xiàn)Socket的讀超時(shí),對(duì)于如果是同步通信的Socket連接來(lái)說(shuō)(底層實(shí)現(xiàn)Connection的可能是同步通信也可能是異步通信),該連接基本上不能使用了,需要關(guān)閉該連接,從新?lián)Q用新的連接,因?yàn)闀?huì)出現(xiàn)請(qǐng)求和響應(yīng)錯(cuò)亂的情況,比如jedis出現(xiàn)的類(lèi)型轉(zhuǎn)換異常,詳見(jiàn)Jedis的類(lèi)型轉(zhuǎn)換異常深究

3 redis實(shí)現(xiàn)分布式鎖

而redis通常可以使用setnx來(lái)實(shí)現(xiàn)分布式鎖

3.1 基本版

1 獲取鎖

public void lock(){for(){ret = setnx lock_ley (current_time + lock_timeout)if(ret){//獲取到了鎖break;}//沒(méi)有獲取到鎖sleep(100);} }

2 釋放鎖

public void release(){del lock_ley }

setnx來(lái)創(chuàng)建一個(gè)key,如果key不存在則創(chuàng)建成功返回1,如果key已經(jīng)存在則返回0。依照上述來(lái)判定是否獲取到了鎖

獲取到鎖的執(zhí)行業(yè)務(wù)邏輯,完畢后刪除lock_key,來(lái)實(shí)現(xiàn)釋放鎖

其他未獲取到鎖的則進(jìn)行不斷重試,直到自己獲取到了鎖

3.2 改進(jìn)版

上述邏輯在正常情況下是OK的,但是一旦獲取到鎖的客戶(hù)端掛了,沒(méi)有執(zhí)行上述釋放鎖的操作,則其他客戶(hù)端就無(wú)法獲取到鎖了,所以在這種情況下有2種方式來(lái)解決:

  • 為lock_key設(shè)置一個(gè)過(guò)期時(shí)間
  • 對(duì)lock_key的value進(jìn)行判斷是否過(guò)期

以第一種為例,在set鍵值的時(shí)候帶上過(guò)期時(shí)間,即使掛了,也會(huì)在過(guò)期時(shí)間之后,其他客戶(hù)端能夠重新競(jìng)爭(zhēng)獲取鎖

public void lock(){while(true){ret = set lock_key identify_value nx ex lock_timeoutif(ret){//獲取到了鎖return;}sleep(100);} }public void release(){value = get lock_keyif(identify_value == value){del lock_key} }

以第二種為例,一旦發(fā)現(xiàn)lock_key的值已經(jīng)小于當(dāng)前時(shí)間了,說(shuō)明該key過(guò)期了,然后對(duì)該key進(jìn)行g(shù)etset設(shè)置,一旦getset返回值是原來(lái)的過(guò)期值,說(shuō)明當(dāng)前客戶(hù)端是第一個(gè)來(lái)操作的,代表獲取到了鎖,一旦getset返回值不是原來(lái)過(guò)期時(shí)間則說(shuō)明前面已經(jīng)有人修改了,則代表沒(méi)有獲取到鎖,詳細(xì)見(jiàn)用Redis實(shí)現(xiàn)分布式鎖,改正如下:

# get lock lock = 0 while lock != 1:timestamp = current_unix_time + lock_timeoutlock = SETNX lock.foo timestampif lock == 1 or (now() > (GET lock.foo) and now() > (GETSET lock.foo timestamp)):break;else:sleep(10ms)# do your job do_job()# release if now() < GET lock.foo:DEL lock.foo

這里看來(lái)第二種其實(shí)沒(méi)有第一種比較好。

3.3 問(wèn)題依舊

問(wèn)題1: lock timeout的存在也使得失去了鎖的意義,即存在并發(fā)的現(xiàn)象。一旦出現(xiàn)鎖的租約時(shí)間,就意味著獲取到鎖的客戶(hù)端必須在租約之內(nèi)執(zhí)行完畢業(yè)務(wù)邏輯,一旦業(yè)務(wù)邏輯執(zhí)行時(shí)間過(guò)長(zhǎng),租約到期,就會(huì)引發(fā)并發(fā)問(wèn)題。所以有l(wèi)ock timeout的可靠性并不是那么的高。

問(wèn)題2: 上述方式僅僅是redis單機(jī)情況下,還存在redis單點(diǎn)故障的問(wèn)題。如果為了解決單點(diǎn)故障而使用redis的sentinel或者cluster方案,則更加復(fù)雜,引入的問(wèn)題更多。

4 ZooKeeper實(shí)現(xiàn)分布式鎖

4.1 案例

這也是ZooKeeper客戶(hù)端curator的分布式鎖實(shí)現(xiàn)。

1 獲取鎖

public void lock(){path = 在父節(jié)點(diǎn)下創(chuàng)建臨時(shí)順序節(jié)點(diǎn)while(true){children = 獲取父節(jié)點(diǎn)的所有節(jié)點(diǎn)if(path是children中的最小的){代表獲取了節(jié)點(diǎn)return;}else{添加監(jiān)控前一個(gè)節(jié)點(diǎn)是否存在的watcherwait();}} }watcher中的內(nèi)容{notifyAll(); }

2 釋放鎖

public void release(){刪除上述創(chuàng)建的節(jié)點(diǎn) }

4.2 總結(jié)

ZooKeeper版本的分布式鎖問(wèn)題相對(duì)比較來(lái)說(shuō)少。

  • 鎖的占用時(shí)間限制:redis就有占用時(shí)間限制,而ZooKeeper則沒(méi)有,最主要的原因是redis目前沒(méi)有辦法知道已經(jīng)獲取鎖的客戶(hù)端的狀態(tài),是已經(jīng)掛了呢還是正在執(zhí)行耗時(shí)較長(zhǎng)的業(yè)務(wù)邏輯。而ZooKeeper通過(guò)臨時(shí)節(jié)點(diǎn)就能清晰知道,如果臨時(shí)節(jié)點(diǎn)存在說(shuō)明還在執(zhí)行業(yè)務(wù)邏輯,如果臨時(shí)節(jié)點(diǎn)不存在說(shuō)明已經(jīng)執(zhí)行完畢釋放鎖或者是掛了。由此看來(lái)redis如果能像ZooKeeper一樣添加一些與客戶(hù)端綁定的臨時(shí)鍵,也是一大好事。
  • 是否單點(diǎn)故障:redis本身有很多中玩法,如客戶(hù)端一致性hash,服務(wù)器端sentinel方案或者cluster方案,很難做到一種分布式鎖方式能應(yīng)對(duì)所有這些方案。而ZooKeeper只有一種玩法,多臺(tái)機(jī)器的節(jié)點(diǎn)數(shù)據(jù)是一致的,沒(méi)有redis的那么多的麻煩因素要考慮。

總體上來(lái)說(shuō)ZooKeeper實(shí)現(xiàn)分布式鎖更加的簡(jiǎn)單,可靠性更高。

5 分布式鎖實(shí)現(xiàn)原理總結(jié)

從上面我們經(jīng)歷了3種實(shí)現(xiàn)方式,可以從中總結(jié)下,該怎么去回答最初提出的問(wèn)題。

5.1 分布式鎖的實(shí)現(xiàn)

在我自己看來(lái)有如下3個(gè)方面:

  • 怎么獲取鎖
  • 怎么釋放鎖
  • 怎么得知鎖被釋放了

5.1.1 怎么獲取鎖

能夠提供一種方式,多個(gè)客戶(hù)端并發(fā)操作,只能有一個(gè)客戶(hù)端能滿(mǎn)足相應(yīng)的要求

如數(shù)據(jù)庫(kù)的for update的sql語(yǔ)句、或者插入一個(gè)含有唯一約束的數(shù)據(jù)等

如redis的setnx等

如ZooKeeper的求最小節(jié)點(diǎn)的方式

這些都可以保證只能有一個(gè)客戶(hù)端獲取到了鎖

5.1.2 怎么釋放鎖

場(chǎng)景一般有2種情況:

  • 1 正常情況下的釋放鎖
  • 2 異常情況下如何釋放鎖(即釋放鎖的操作沒(méi)有被執(zhí)行,如掛掉、沒(méi)執(zhí)行成功等原因)

如redis正常情況下釋放鎖是刪除lock_key,異常情況下,只能通過(guò)lock_key的超時(shí)時(shí)間了

如ZooKeeper正常情況下釋放鎖是刪除臨時(shí)節(jié)點(diǎn),異常情況下,服務(wù)器也會(huì)主動(dòng)刪除臨時(shí)節(jié)點(diǎn)(這種機(jī)制就簡(jiǎn)單多了)

5.1.3 怎么得知鎖被釋放了

實(shí)現(xiàn)方式一般有2種情況:

  • 1 沒(méi)有獲取到鎖的客戶(hù)端不斷嘗試獲取鎖
  • 2 服務(wù)器端通知客戶(hù)端鎖被釋放了

當(dāng)然第二種情況是最優(yōu)的(客戶(hù)端所做的無(wú)用功最少),如ZooKeeper通過(guò)注冊(cè)watcher來(lái)得到鎖釋放的通知。而數(shù)據(jù)庫(kù)、redis沒(méi)有辦法來(lái)通知客戶(hù)端鎖釋放了,那客戶(hù)端就只能傻傻的不斷嘗試獲取鎖了。

歡迎來(lái)拍磚,相互討論,我相信會(huì)越辯越清晰。

總結(jié)

以上是生活随笔為你收集整理的分布式锁的实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。