chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

分布式鎖的設(shè)計(jì)與實(shí)現(xiàn)

數(shù)據(jù)分析與開(kāi)發(fā) ? 來(lái)源:撿田螺的小男孩 ? 作者:撿田螺的小男孩 ? 2022-05-13 15:36 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

前言

今天跟大家探討一下分布式鎖的設(shè)計(jì)與實(shí)現(xiàn)。希望對(duì)大家有幫助,如果有不正確的地方,歡迎指出,一起學(xué)習(xí),一起進(jìn)步哈。

  • 分布式鎖概述
  • 數(shù)據(jù)庫(kù)分布式鎖
  • Redis分布式鎖
  • Zookeeper分布式鎖
  • 三種分布式鎖對(duì)比

1. 分布式鎖概述

我們的系統(tǒng)都是分布式部署的,日常開(kāi)發(fā)中,秒殺下單、搶購(gòu)商品等等業(yè)務(wù)場(chǎng)景,為了防?庫(kù)存超賣(mài),都需要用到分布式鎖

分布式鎖其實(shí)就是,控制分布式系統(tǒng)不同進(jìn)程共同訪問(wèn)共享資源的一種鎖的實(shí)現(xiàn)。如果不同的系統(tǒng)或同一個(gè)系統(tǒng)的不同主機(jī)之間共享了某個(gè)臨界資源,往往需要互斥來(lái)防止彼此干擾,以保證一致性。

業(yè)界流行的分布式鎖實(shí)現(xiàn),一般有這3種方式:

  • 基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)的分布式鎖
  • 基于Redis實(shí)現(xiàn)的分布式鎖
  • 基于Zookeeper實(shí)現(xiàn)的分布式鎖

2. 基于數(shù)據(jù)庫(kù)的分布式鎖

2.1 數(shù)據(jù)庫(kù)悲觀鎖實(shí)現(xiàn)的分布式鎖

可以使用select ... for update來(lái)實(shí)現(xiàn)分布式鎖。我們自己的項(xiàng)目,分布式定時(shí)任務(wù),就使用類(lèi)似的實(shí)現(xiàn)方案,我給大家來(lái)展示個(gè)簡(jiǎn)單版的哈

表結(jié)構(gòu)如下:

CREATETABLE`t_resource_lock`(
`key_resource`varchar(45)COLLATEutf8_binNOTNULLDEFAULT'資源主鍵',
`status`char(1)COLLATEutf8_binNOTNULLDEFAULT''COMMENT'S,F,P',
`lock_flag`int(10)unsignedNOTNULLDEFAULT'0'COMMENT'1是已經(jīng)鎖0是未鎖',
`begin_time`datetimeDEFAULTNULLCOMMENT'開(kāi)始時(shí)間',
`end_time`datetimeDEFAULTNULLCOMMENT'結(jié)束時(shí)間',
`client_ip`varchar(45)COLLATEutf8_binNOTNULLDEFAULT'搶到鎖的IP',
`time`int(10)unsignedNOTNULLDEFAULT'60'COMMENT'方法生命周期內(nèi)只允許一個(gè)結(jié)點(diǎn)獲取一次鎖,單位:分鐘',
PRIMARYKEY(`key_resource`)USINGBTREE
)ENGINE=InnoDBDEFAULTCHARSET=utf8COLLATE=utf8_bin

加鎖lock方法的偽代碼如下:

@Transcational//一定要加事務(wù)
publicbooleanlock(StringkeyResource,inttime){
resourceLock='select*fromt_resource_lockwherekey_resource='#{keySource}'forupdate';

try{
if(resourceLock==null){
//插入鎖的數(shù)據(jù)
resourceLock=newResourceLock();
resourceLock.setTime(time);
resourceLock.setLockFlag(1);//上鎖
resourceLock.setStatus(P);//處理中
resourceLock.setBeginTime(newDate());
intcount="insertintoresourceLock";
if(count==1){
//獲取鎖成功
returntrue;
}
returnfalse;
}
}catch(Exceptionx){
returnfalse;
}

//沒(méi)上鎖并且鎖已經(jīng)超時(shí),即可以獲取鎖成功
if(resourceLock.getLockFlag=='0'&&'S'.equals(resourceLock.getstatus)
&&newDate()>=resourceLock.addDateTime(resourceLock.getBeginTime(,time)){
resourceLock.setLockFlag(1);//上鎖
resourceLock.setStatus(P);//處理中
resourceLock.setBeginTime(newDate());
//updateresourceLock;
returntrue;
}elseif(newDate()>=resourceLock.addDateTime(resourceLock.getBeginTime(,time)){
//超時(shí)未正常執(zhí)行結(jié)束,獲取鎖失敗
returnfalse;
}else{
returnfalse;
}
}

解鎖unlock方法的偽代碼如下:

publicvoidunlock(Stringv,status){
resourceLock.setLockFlag(0);//解鎖
resourceLock.setStatus(status);S:表示成功,F(xiàn)表示失敗
//updateresourceLock;
return;
}

整體流程:

try{
if(lock(keyResource,time)){//加鎖
 status = process();//你的業(yè)務(wù)邏輯處理。
}
}finally{
unlock(keyResource,status);//釋放鎖
}

其實(shí)這個(gè)悲觀鎖實(shí)現(xiàn)的分布式鎖,整體的流程還是比較清晰的。就是先select ... for update鎖住主鍵key_resource那個(gè)記錄,如果為空,則可以插入一條記錄,如果已有記錄判斷下狀態(tài)和時(shí)間是否已經(jīng)超時(shí)。這里需要注意一下哈,必須要加事務(wù)哈。

2.2 數(shù)據(jù)庫(kù)樂(lè)觀鎖實(shí)現(xiàn)的分布式鎖

除了悲觀鎖,還可以用樂(lè)觀鎖實(shí)現(xiàn)分布式鎖。樂(lè)觀鎖,顧名思義,就是很樂(lè)觀,每次更新操作,都覺(jué)得不會(huì)存在并發(fā)沖突,只有更新失敗后,才重試。它是基于CAS思想實(shí)現(xiàn)的。我以前的公司,扣減余額就是用這種方案。

搞個(gè)version字段,每次更新修改,都會(huì)自增加一,然后去更新余額時(shí),把查出來(lái)的那個(gè)版本號(hào),帶上條件去更新,如果是上次那個(gè)版本號(hào),就更新,如果不是,表示別人并發(fā)修改過(guò)了,就繼續(xù)重試。

大概流程如下:

  1. 查詢(xún)版本號(hào)和余額
selectversion,balancefromaccountwhereuser_id='666';

假設(shè)查到版本號(hào)是oldVersion=1.

  1. 邏輯處理,判斷余額
if(balance<扣減金額){
???return;
}

left_balance=balance-扣減金額;
  1. 進(jìn)行扣減余額
updateaccountsetbalance=#{left_balance},version=version+1whereversion
=#{oldVersion}andbalance>=#{left_balance} and user_id='666';

大家可以看下這個(gè)流程圖哈:

7f98b3b2-c276-11ec-bce3-dac502259ad0.png

這種方式適合并發(fā)不高的場(chǎng)景,一般需要設(shè)置一下重試的次數(shù)

3.基于Redis實(shí)現(xiàn)的分布式鎖

Redis分布式鎖一般有以下這幾種實(shí)現(xiàn)方式:

  • setnx + expire
  • setnx + value值是過(guò)期時(shí)間
  • set的擴(kuò)展命令(set ex px nx)
  • set ex px nx + 校驗(yàn)唯一隨機(jī)值,再刪除
  • Redisson
  • Redisson + RedLock

3.1 setnx + expire

聊到Redis分布式鎖,很多小伙伴反手就是setnx + expire,如下:

if(jedis.setnx(key,lock_value)==1){//setnx加鎖
expire(key,100);//設(shè)置過(guò)期時(shí)間
try{
dosomething//業(yè)務(wù)處理
}catch(){
}
finally{
jedis.del(key);//釋放鎖
}
}

這段代碼是可以加鎖成功,但是你有沒(méi)有發(fā)現(xiàn)問(wèn)題,加鎖操作和設(shè)置超時(shí)時(shí)間是分開(kāi)的。假設(shè)在執(zhí)行完setnx加鎖后,正要執(zhí)行expire設(shè)置過(guò)期時(shí)間時(shí),進(jìn)程crash掉或者要重啟維護(hù)了,那這個(gè)鎖就長(zhǎng)生不老了,別的線(xiàn)程永遠(yuǎn)獲取不到鎖啦,所以分布式鎖不能這么實(shí)現(xiàn)!

3.2 setnx + value值是過(guò)期時(shí)間

longexpires=System.currentTimeMillis()+expireTime;//系統(tǒng)時(shí)間+設(shè)置的過(guò)期時(shí)間
StringexpiresStr=String.valueOf(expires);

//如果當(dāng)前鎖不存在,返回加鎖成功
if(jedis.setnx(key,expiresStr)==1){
returntrue;
}
//如果鎖已經(jīng)存在,獲取鎖的過(guò)期時(shí)間
StringcurrentValueStr=jedis.get(key);

//如果獲取到的過(guò)期時(shí)間,小于系統(tǒng)當(dāng)前時(shí)間,表示已經(jīng)過(guò)期
if(currentValueStr!=null&&Long.parseLong(currentValueStr)if(oldValueStr!=null&&oldValueStr.equals(currentValueStr)){
//考慮多線(xiàn)程并發(fā)的情況,只有一個(gè)線(xiàn)程的設(shè)置值和當(dāng)前值相同,它才可以加鎖
returntrue;
}
}

//其他情況,均返回加鎖失敗
returnfalse;
}

日常開(kāi)發(fā)中,有些小伙伴就是這么實(shí)現(xiàn)分布式鎖的,但是會(huì)有這些缺點(diǎn)

  • 過(guò)期時(shí)間是客戶(hù)端自己生成的,分布式環(huán)境下,每個(gè)客戶(hù)端的時(shí)間必須同步。
  • 沒(méi)有保存持有者的唯一標(biāo)識(shí),可能被別的客戶(hù)端釋放/解鎖
  • 鎖過(guò)期的時(shí)候,并發(fā)多個(gè)客戶(hù)端同時(shí)請(qǐng)求過(guò)來(lái),都執(zhí)行了jedis.getSet(),最終只能有一個(gè)客戶(hù)端加鎖成功,但是該客戶(hù)端鎖的過(guò)期時(shí)間,可能被別的客戶(hù)端覆蓋。

3.3 set的擴(kuò)展命令(set ex px nx)

這個(gè)命令的幾個(gè)參數(shù)分別表示什么意思呢?跟大家復(fù)習(xí)一下:

SETkeyvalue[EXseconds][PXmilliseconds][NX|XX]
  • EX second :設(shè)置鍵的過(guò)期時(shí)間為second秒。
  • PX millisecond :設(shè)置鍵的過(guò)期時(shí)間為millisecond毫秒。
  • NX :只在鍵不存在時(shí),才對(duì)鍵進(jìn)行設(shè)置操作。
  • XX :只在鍵已經(jīng)存在時(shí),才對(duì)鍵進(jìn)行設(shè)置操作。
if(jedis.set(key,lock_value,"NX","EX",100s)==1){//加鎖
try{
dosomething//業(yè)務(wù)處理
}catch(){
}
finally{
jedis.del(key);//釋放鎖
}
}

這個(gè)方案可能存在這樣的問(wèn)題:

  • 鎖過(guò)期釋放了,業(yè)務(wù)還沒(méi)執(zhí)行完。
  • 鎖被別的線(xiàn)程誤刪。

有些伙伴可能會(huì)有個(gè)疑問(wèn),就是鎖為什么會(huì)被別的線(xiàn)程誤刪呢?假設(shè)并發(fā)多線(xiàn)程場(chǎng)景下,線(xiàn)程A獲得了鎖,但是它沒(méi)釋放鎖的話(huà),線(xiàn)程B是獲取不到鎖的,所以按道理它是執(zhí)行不到加鎖下面的代碼滴,怎么會(huì)導(dǎo)致鎖被別的線(xiàn)程誤刪呢?

假設(shè)線(xiàn)程A和B,都想用key加鎖,最后A搶到鎖加鎖成功,但是由于執(zhí)行業(yè)務(wù)邏輯的耗時(shí)很長(zhǎng),超過(guò)了設(shè)置的超時(shí)時(shí)間100s。這時(shí)候,Redis就自動(dòng)釋放了key鎖。這時(shí)候線(xiàn)程B就可以加鎖成功了,接下啦,它也執(zhí)行業(yè)務(wù)邏輯處理。假設(shè)碰巧這時(shí)候,A執(zhí)行完自己的業(yè)務(wù)邏輯,它就去釋放鎖,但是它就把B的鎖給釋放了。

3.4 set ex px nx + 校驗(yàn)唯一隨機(jī)值,再刪除

為了解決鎖被別的線(xiàn)程誤刪問(wèn)題??梢栽?code style="margin-right:2px;margin-left:2px;padding:2px 4px;max-width:100%;font-size:14px;background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(239,112,96);">set ex px nx的基礎(chǔ)上,加上個(gè)校驗(yàn)的唯一隨機(jī)值,如下:

if(jedis.set(key,uni_request_id,"NX","EX",100s)==1){//加鎖
try{
dosomething//業(yè)務(wù)處理
}catch(){
}
finally{
//判斷是不是當(dāng)前線(xiàn)程加的鎖,是才釋放
if(uni_request_id.equals(jedis.get(key))){
jedis.del(key);//釋放鎖
}
}
}

在這里,判斷當(dāng)前線(xiàn)程加的鎖和釋放鎖不是一個(gè)原子操作。如果調(diào)用jedis.del()釋放鎖的時(shí)候,可能這把鎖已經(jīng)不屬于當(dāng)前客戶(hù)端,會(huì)解除他人加的鎖。

一般可以用lua腳本來(lái)包一下。lua腳本如下:

ifredis.call('get',KEYS[1])==ARGV[1]then
returnredis.call('del',KEYS[1])
else
return0
end;

這種方式比較不錯(cuò)了,一般情況下,已經(jīng)可以使用這種實(shí)現(xiàn)方式。但是還是存在:鎖過(guò)期釋放了,業(yè)務(wù)還沒(méi)執(zhí)行完的問(wèn)題。

3.5 Redisson

對(duì)于可能存在鎖過(guò)期釋放,業(yè)務(wù)沒(méi)執(zhí)行完的問(wèn)題。我們可以稍微把鎖過(guò)期時(shí)間設(shè)置長(zhǎng)一些,大于正常業(yè)務(wù)處理時(shí)間就好啦。如果你覺(jué)得不是很穩(wěn),還可以給獲得鎖的線(xiàn)程,開(kāi)啟一個(gè)定時(shí)守護(hù)線(xiàn)程,每隔一段時(shí)間檢查鎖是否還存在,存在則對(duì)鎖的過(guò)期時(shí)間延長(zhǎng),防止鎖過(guò)期提前釋放。

當(dāng)前開(kāi)源框架Redisson解決了這個(gè)問(wèn)題??梢钥聪翿edisson底層原理圖:

7fba4022-c276-11ec-bce3-dac502259ad0.png

只要線(xiàn)程一加鎖成功,就會(huì)啟動(dòng)一個(gè)watch dog看門(mén)狗,它是一個(gè)后臺(tái)線(xiàn)程,會(huì)每隔10秒檢查一下,如果線(xiàn)程1還持有鎖,那么就會(huì)不斷的延長(zhǎng)鎖key的生存時(shí)間。因此,Redisson就是使用watch dog解決了鎖過(guò)期釋放,業(yè)務(wù)沒(méi)執(zhí)行完問(wèn)題。

3.6 Redisson + RedLock

前面六種方案都只是基于Redis單機(jī)版的分布式鎖討論,還不是很完美。因?yàn)?strong style="max-width:100%;">Redis一般都是集群部署的:

7fcfa048-c276-11ec-bce3-dac502259ad0.png

如果線(xiàn)程一在Redismaster節(jié)點(diǎn)上拿到了鎖,但是加鎖的key還沒(méi)同步到slave節(jié)點(diǎn)。恰好這時(shí),master節(jié)點(diǎn)發(fā)生故障,一個(gè)slave節(jié)點(diǎn)就會(huì)升級(jí)為master節(jié)點(diǎn)。線(xiàn)程二就可以順理成章獲取同個(gè)key的鎖啦,但線(xiàn)程一也已經(jīng)拿到鎖了,鎖的安全性就沒(méi)了。

為了解決這個(gè)問(wèn)題,Redis作者antirez提出一種高級(jí)的分布式鎖算法Redlock。它的核心思想是這樣的:

部署多個(gè)Redis master,以保證它們不會(huì)同時(shí)宕掉。并且這些master節(jié)點(diǎn)是完全相互獨(dú)立的,相互之間不存在數(shù)據(jù)同步。同時(shí),需要確保在這多個(gè)master實(shí)例上,是與在Redis單實(shí)例,使用相同方法來(lái)獲取和釋放鎖。

我們假設(shè)當(dāng)前有5個(gè)Redis master節(jié)點(diǎn),在5臺(tái)服務(wù)器上面運(yùn)行這些Redis實(shí)例。

7feb0018-c276-11ec-bce3-dac502259ad0.png

RedLock的實(shí)現(xiàn)步驟:

  1. 獲取當(dāng)前時(shí)間,以毫秒為單位。
  2. 按順序向5個(gè)master節(jié)點(diǎn)請(qǐng)求加鎖。客戶(hù)端設(shè)置網(wǎng)絡(luò)連接和響應(yīng)超時(shí)時(shí)間,并且超時(shí)時(shí)間要小于鎖的失效時(shí)間。(假設(shè)鎖自動(dòng)失效時(shí)間為10秒,則超時(shí)時(shí)間一般在5-50毫秒之間,我們就假設(shè)超時(shí)時(shí)間是50ms吧)。如果超時(shí),跳過(guò)該master節(jié)點(diǎn),盡快去嘗試下一個(gè)master節(jié)點(diǎn)。
  3. 客戶(hù)端使用當(dāng)前時(shí)間減去開(kāi)始獲取鎖時(shí)間(即步驟1記錄的時(shí)間),得到獲取鎖使用的時(shí)間。當(dāng)且僅當(dāng)超過(guò)一半(N/2+1,這里是5/2+1=3個(gè)節(jié)點(diǎn))的Redis master節(jié)點(diǎn)都獲得鎖,并且使用的時(shí)間小于鎖失效時(shí)間時(shí),鎖才算獲取成功。(如上圖,10s> 30ms+40ms+50ms+4m0s+50ms)
  4. 如果取到了鎖,key的真正有效時(shí)間就變啦,需要減去獲取鎖所使用的時(shí)間。
  5. 如果獲取鎖失?。](méi)有在至少N/2+1個(gè)master實(shí)例取到鎖,有或者獲取鎖時(shí)間已經(jīng)超過(guò)了有效時(shí)間),客戶(hù)端要在所有的master節(jié)點(diǎn)上解鎖(即便有些master節(jié)點(diǎn)根本就沒(méi)有加鎖成功,也需要解鎖,以防止有些漏網(wǎng)之魚(yú))。

簡(jiǎn)化下步驟就是:

  • 按順序向5個(gè)master節(jié)點(diǎn)請(qǐng)求加鎖
  • 根據(jù)設(shè)置的超時(shí)時(shí)間來(lái)判斷,是不是要跳過(guò)該master節(jié)點(diǎn)。
  • 如果大于等于3個(gè)節(jié)點(diǎn)加鎖成功,并且使用的時(shí)間小于鎖的有效期,即可認(rèn)定加鎖成功啦。
  • 如果獲取鎖失敗,解鎖!

Redisson實(shí)現(xiàn)了redLock版本的鎖,有興趣的小伙伴,可以去了解一下哈~

4.Zookeeper分布式鎖

在學(xué)習(xí)Zookeeper分布式鎖之前,我們復(fù)習(xí)一下Zookeeper的節(jié)點(diǎn)哈。

Zookeeper的節(jié)點(diǎn)Znode有四種類(lèi)型:

  • 持久節(jié)點(diǎn):默認(rèn)的節(jié)點(diǎn)類(lèi)型。創(chuàng)建節(jié)點(diǎn)的客戶(hù)端與zookeeper斷開(kāi)連接后,該節(jié)點(diǎn)依舊存在。
  • 持久節(jié)點(diǎn)順序節(jié)點(diǎn):所謂順序節(jié)點(diǎn),就是在創(chuàng)建節(jié)點(diǎn)時(shí),Zookeeper根據(jù)創(chuàng)建的時(shí)間順序給該節(jié)點(diǎn)名稱(chēng)進(jìn)行編號(hào),持久節(jié)點(diǎn)順序節(jié)點(diǎn)就是有順序的持久節(jié)點(diǎn)。
  • 臨時(shí)節(jié)點(diǎn):和持久節(jié)點(diǎn)相反,當(dāng)創(chuàng)建節(jié)點(diǎn)的客戶(hù)端與zookeeper斷開(kāi)連接后,臨時(shí)節(jié)點(diǎn)會(huì)被刪除。
  • 臨時(shí)順序節(jié)點(diǎn):有順序的臨時(shí)節(jié)點(diǎn)。

Zookeeper分布式鎖實(shí)現(xiàn)應(yīng)用了臨時(shí)順序節(jié)點(diǎn)。這里不貼代碼啦,來(lái)講下zk分布式鎖的實(shí)現(xiàn)原理吧。

4.1 zk獲取鎖過(guò)程

當(dāng)?shù)谝粋€(gè)客戶(hù)端請(qǐng)求過(guò)來(lái)時(shí),Zookeeper客戶(hù)端會(huì)創(chuàng)建一個(gè)持久節(jié)點(diǎn)locks。如果它(Client1)想獲得鎖,需要在locks節(jié)點(diǎn)下創(chuàng)建一個(gè)順序節(jié)點(diǎn)lock1.如圖

80064bb6-c276-11ec-bce3-dac502259ad0.png

接著,客戶(hù)端Client1會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),判斷自己的節(jié)點(diǎn)lock1是不是排序最小的那一個(gè),如果是,則成功獲得鎖。

801faf7a-c276-11ec-bce3-dac502259ad0.png

這時(shí)候如果又來(lái)一個(gè)客戶(hù)端client2前來(lái)嘗試獲得鎖,它會(huì)在locks下再創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn)lock2

80360bda-c276-11ec-bce3-dac502259ad0.png

客戶(hù)端client2一樣也會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),判斷自己的節(jié)點(diǎn)lock2是不是最小的,此時(shí),發(fā)現(xiàn)lock1才是最小的,于是獲取鎖失敗。獲取鎖失敗,它是不會(huì)甘心的,client2向它排序靠前的節(jié)點(diǎn)lock1注冊(cè)Watcher事件,用來(lái)監(jiān)聽(tīng)lock1是否存在,也就是說(shuō)client2搶鎖失敗進(jìn)入等待狀態(tài)。

804e8dfe-c276-11ec-bce3-dac502259ad0.png

此時(shí),如果再來(lái)一個(gè)客戶(hù)端Client3來(lái)嘗試獲取鎖,它會(huì)在locks下再創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn)lock3

80660bfa-c276-11ec-bce3-dac502259ad0.png

同樣的,client3一樣也會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),判斷自己的節(jié)點(diǎn)lock3是不是最小的,發(fā)現(xiàn)自己不是最小的,就獲取鎖失敗。它也是不會(huì)甘心的,它會(huì)向在它前面的節(jié)點(diǎn)lock2注冊(cè)Watcher事件,以監(jiān)聽(tīng)lock2節(jié)點(diǎn)是否存在。807bd8fe-c276-11ec-bce3-dac502259ad0.png

4.2 釋放鎖

我們?cè)賮?lái)看看釋放鎖的流程,Zookeeper的客戶(hù)端業(yè)務(wù)完成或者發(fā)生故障,都會(huì)刪除臨時(shí)節(jié)點(diǎn),釋放鎖。如果是任務(wù)完成,Client1會(huì)顯式調(diào)用刪除lock1的指令

809e3534-c276-11ec-bce3-dac502259ad0.png

如果是客戶(hù)端故障了,根據(jù)臨時(shí)節(jié)點(diǎn)得特性,lock1是會(huì)自動(dòng)刪除的

80c249c4-c276-11ec-bce3-dac502259ad0.png

lock1節(jié)點(diǎn)被刪除后,Client2可開(kāi)心了,因?yàn)樗恢北O(jiān)聽(tīng)著lock1。lock1節(jié)點(diǎn)刪除,Client2立刻收到通知,也會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),發(fā)下lock2是最小,就獲得鎖。

80d48ac6-c276-11ec-bce3-dac502259ad0.png

同理,Client2獲得鎖之后,Client3也對(duì)它虎視眈眈,啊哈哈~

  • Zookeeper設(shè)計(jì)定位就是分布式協(xié)調(diào),簡(jiǎn)單易用。如果獲取不到鎖,只需添加一個(gè)監(jiān)聽(tīng)器即可,很適合做分布式鎖。
  • Zookeeper作為分布式鎖也缺點(diǎn):如果有很多的客戶(hù)端頻繁的申請(qǐng)加鎖、釋放鎖,對(duì)于Zookeeper集群的壓力會(huì)比較大。

5. 三種分布式鎖對(duì)比

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

優(yōu)點(diǎn):

  • 簡(jiǎn)單,使用方便,不需要引入Redis、zookeeper等中間件。

缺點(diǎn):

  • 不適合高并發(fā)的場(chǎng)景
  • db操作性能較差;

5.2 Redis分布式鎖實(shí)現(xiàn)

優(yōu)點(diǎn):

  • 性能好,適合高并發(fā)場(chǎng)景
  • 較輕量級(jí)
  • 有較好的框架支持,如Redisson

缺點(diǎn):

  • 過(guò)期時(shí)間不好控制
  • 需要考慮鎖被別的線(xiàn)程誤刪場(chǎng)景

5.3 Zookeeper分布式鎖實(shí)現(xiàn)

缺點(diǎn):

  • 性能不如redis實(shí)現(xiàn)的分布式鎖
  • 比較重的分布式鎖。

優(yōu)點(diǎn):

  • 有較好的性能和可靠性
  • 有封裝較好的框架,如Curator

5.4 對(duì)比匯總

  • 從性能角度(從高到低)Redis > Zookeeper >= 數(shù)據(jù)庫(kù);
  • 從理解的難易程度角度(從低到高)數(shù)據(jù)庫(kù) > Redis > Zookeeper;
  • 從實(shí)現(xiàn)的復(fù)雜性角度(從低到高)Zookeeper > Redis > 數(shù)據(jù)庫(kù);
  • 從可靠性角度(從高到低)Zookeeper > Redis > 數(shù)據(jù)庫(kù)。

原文標(biāo)題:面試必備:聊聊分布式鎖的多種實(shí)現(xiàn)!

文章出處:【微信公眾號(hào):數(shù)據(jù)分析與開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)庫(kù)
    +關(guān)注

    關(guān)注

    7

    文章

    4078

    瀏覽量

    68520
  • 分布式
    +關(guān)注

    關(guān)注

    1

    文章

    1107

    瀏覽量

    76694
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    394

    瀏覽量

    12248

原文標(biāo)題:面試必備:聊聊分布式鎖的多種實(shí)現(xiàn)!

文章出處:【微信號(hào):DBDevs,微信公眾號(hào):數(shù)據(jù)分析與開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    #硬聲創(chuàng)作季 13-分布式實(shí)現(xiàn)方案(Zookeeper方式實(shí)現(xiàn)

    JAVA編程語(yǔ)言
    Mr_haohao
    發(fā)布于 :2022年09月03日 08:12:43

    #硬聲創(chuàng)作季 14-分布式實(shí)現(xiàn)方案(Zookeeper方式實(shí)現(xiàn)

    JAVA編程語(yǔ)言
    Mr_haohao
    發(fā)布于 :2022年09月03日 08:13:28

    #硬聲創(chuàng)作季 15-分布式實(shí)現(xiàn)方案(Zookeeper方式實(shí)現(xiàn)

    JAVA編程語(yǔ)言
    Mr_haohao
    發(fā)布于 :2022年09月03日 08:14:06

    #硬聲創(chuàng)作季 16-分布式實(shí)現(xiàn)方案(Zookeeper方式實(shí)現(xiàn)

    JAVA編程語(yǔ)言
    Mr_haohao
    發(fā)布于 :2022年09月03日 08:14:44

    #硬聲創(chuàng)作季 20-分布式實(shí)現(xiàn)方案(Redis方式實(shí)現(xiàn)分布式

    編程語(yǔ)言
    Mr_haohao
    發(fā)布于 :2022年09月03日 08:17:19

    #硬聲創(chuàng)作季 SSG分布式視頻教程:62.ZooKeeper分布式實(shí)現(xiàn)思路

    數(shù)據(jù)庫(kù)MySQL
    Mr_haohao
    發(fā)布于 :2022年10月02日 23:31:51

    #硬聲創(chuàng)作季 SSG分布式視頻教程:64.ZooKeeper分布式的基本實(shí)現(xiàn)

    數(shù)據(jù)庫(kù)MySQL
    Mr_haohao
    發(fā)布于 :2022年10月02日 23:32:31

    #硬聲創(chuàng)作季 SSG分布式視頻教程:65.測(cè)試ZooKeeper分布式的基本實(shí)現(xiàn)

    數(shù)據(jù)庫(kù)分布式MySQL
    Mr_haohao
    發(fā)布于 :2022年10月02日 23:33:05

    Redis實(shí)戰(zhàn)篇-10.分布式-Redis的分布式實(shí)現(xiàn)

    Redis
    電子學(xué)習(xí)
    發(fā)布于 :2023年01月07日 15:18:09

    分析3種分布式的設(shè)計(jì)與實(shí)現(xiàn)

    對(duì)于高可用性,一般可以通過(guò)集群或者master-slave來(lái)解決,redis優(yōu)勢(shì)是性能出色,劣勢(shì)就是由于數(shù)據(jù)在內(nèi)存中,一旦緩存服務(wù)宕機(jī),數(shù)據(jù)就丟失了。像redis自帶復(fù)制功能,可以對(duì)數(shù)據(jù)可靠性有
    的頭像 發(fā)表于 11-26 11:26 ?3512次閱讀
    分析3種<b class='flag-5'>分布式</b><b class='flag-5'>鎖</b>的設(shè)計(jì)與<b class='flag-5'>實(shí)現(xiàn)</b>

    探討一下分布式的設(shè)計(jì)與實(shí)現(xiàn)

    我們?cè)賮?lái)看看釋放的流程,Zookeeper的客戶(hù)端業(yè)務(wù)完成或者發(fā)生故障,都會(huì)刪除臨時(shí)節(jié)點(diǎn),釋放。如果是任務(wù)完成,Client1會(huì)顯調(diào)用刪除lock1的指令
    的頭像 發(fā)表于 12-28 10:24 ?1179次閱讀

    redis分布式如何實(shí)現(xiàn)

    Redis分布式是一種基于Redis實(shí)現(xiàn)的機(jī)制,可以用于多個(gè)進(jìn)程或多臺(tái)服務(wù)器之間對(duì)共享資源的并發(fā)訪問(wèn)控制。在分布式系統(tǒng)中,由于多個(gè)進(jìn)程或多臺(tái)服務(wù)器同時(shí)訪問(wèn)共享資源,可能會(huì)發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)
    的頭像 發(fā)表于 11-16 11:29 ?1359次閱讀

    redis分布式如何實(shí)現(xiàn)等待

    Redis是一種高性能的鍵值存儲(chǔ)系統(tǒng),它除了提供基本的數(shù)據(jù)緩存功能外,還支持一些復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和功能,例如發(fā)布訂閱、事務(wù)、持久化等。其中,Redis的分布式是其常用的功能之一,可以用于解決多個(gè)
    的頭像 發(fā)表于 11-16 11:31 ?1851次閱讀

    Java手寫(xiě)分布式實(shí)現(xiàn)

    隨著互聯(lián)網(wǎng)業(yè)務(wù)的發(fā)展,原本單機(jī)部署的系統(tǒng)演化成如今的分布式集群系統(tǒng)后,由于分布式系統(tǒng)多線(xiàn)程
    的頭像 發(fā)表于 11-17 15:51 ?1520次閱讀
    Java手寫(xiě)<b class='flag-5'>分布式</b><b class='flag-5'>鎖</b>的<b class='flag-5'>實(shí)現(xiàn)</b>