欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2023年04月09日 15:11:40   作者:玄郭郭  
這篇文章主要介紹了redis如何實(shí)現(xiàn)分布式鎖,文章中有詳細(xì)的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

一、前言

為什么需要分布式鎖?

在我們的日常開(kāi)發(fā)中,一個(gè)進(jìn)程中當(dāng)多線程的去競(jìng)爭(zhēng)某一資源的時(shí)候,我們通常會(huì)用一把鎖來(lái)保證只有一個(gè)線程獲取到資源。如加上synchronize關(guān)鍵字或ReentrantLock鎖等操作。

那么,如果是多個(gè)進(jìn)程相互競(jìng)爭(zhēng)一個(gè)資源,如何保證資源只會(huì)被一個(gè)操作者持有呢?

例如:微服務(wù)的架構(gòu)下,多個(gè)應(yīng)用服務(wù)要同時(shí)對(duì)同一條數(shù)據(jù)做修改,那么要確保數(shù)據(jù)的正確性,就只能有一個(gè)應(yīng)用修改成功。

server1、server2、server3 這三個(gè)服務(wù)都要修改amount這個(gè)數(shù)據(jù),每個(gè)服務(wù)更新的值不同,為了保證數(shù)據(jù)的正確性,三個(gè)服務(wù)都向lock server服務(wù)申請(qǐng)修改權(quán)限,最終server2拿到了修改權(quán)限,即server2將amount更新為2,其他服務(wù)由于沒(méi)有獲取到修改權(quán)限則返回更新失敗。

二、基于redis實(shí)現(xiàn)分布式鎖

為什么redis可以實(shí)現(xiàn)分布式鎖?

因?yàn)閞edis是一個(gè)單獨(dú)的非業(yè)務(wù)服務(wù),不會(huì)受到其他業(yè)務(wù)服務(wù)的限制,所有的業(yè)務(wù)服務(wù)都可以向redis發(fā)送寫(xiě)入命令,且只有一個(gè)業(yè)務(wù)服務(wù)可以寫(xiě)入命令成功,那么這個(gè)寫(xiě)入命令成功的服務(wù)即獲得了鎖,可以進(jìn)行后續(xù)對(duì)資源的操作,其他未寫(xiě)入成功的服務(wù),則進(jìn)行其他處理。

如何實(shí)現(xiàn)?

redis的String類(lèi)型就可以實(shí)現(xiàn)。

鎖的獲取

setnx命令:表示SET if Not eXists,即如果 key 不存在,才會(huì)設(shè)置它的值,否則什么也不做。

兩個(gè)客戶端同時(shí)向redis寫(xiě)入try_lock,客戶端1寫(xiě)入成功,即獲取分布式鎖成功??蛻舳?寫(xiě)入失敗,則獲取分布式鎖失敗。

鎖的釋放

當(dāng)客戶端1操作完后,釋放鎖資源,即刪除try_lock。那么此時(shí)客戶端2再次嘗試獲取鎖時(shí),則會(huì)獲取鎖成功。

那么這樣分布式鎖就這樣結(jié)束了?不不不,現(xiàn)實(shí)往往有很多情況出現(xiàn)。

假如客戶端1在獲取到鎖資源后,服務(wù)宕機(jī)了,那么這個(gè)try_lock會(huì)一直存在redis中,那么其他服務(wù)就永遠(yuǎn)無(wú)法獲取到鎖了。

如何解決這個(gè)問(wèn)題呢?

三、如何避免死鎖?鎖的過(guò)期時(shí)間如何設(shè)置?

避免死鎖

設(shè)置鍵過(guò)期時(shí)間,超過(guò)這個(gè)時(shí)間即給key刪除掉。

這樣的話,就算當(dāng)前服務(wù)獲取到鎖后宕機(jī)了,這個(gè)key也會(huì)在一定時(shí)間后被刪除,其他服務(wù)照樣可以繼續(xù)獲取鎖。

給serverLock鍵設(shè)置一個(gè)10秒的過(guò)期時(shí)間,10秒后會(huì)自動(dòng)刪除該鍵。

這樣雖然解決了上面說(shuō)的問(wèn)題,但是又會(huì)引入新的問(wèn)題。

假如服務(wù)A加鎖成功,鎖會(huì)在10s后自動(dòng)釋放,但由于業(yè)務(wù)復(fù)雜,執(zhí)行時(shí)間過(guò)長(zhǎng),10s內(nèi)還沒(méi)執(zhí)行完,此時(shí)鎖已經(jīng)被redis自動(dòng)釋放掉了。此時(shí)服務(wù)B就重新獲取到了該鎖,服務(wù)B開(kāi)始執(zhí)行他的業(yè)務(wù),服務(wù)A在執(zhí)行到第12s的時(shí)候執(zhí)行完了,那么服務(wù)A會(huì)去釋放鎖,則此時(shí)釋放的卻是服務(wù)B剛獲取到的鎖。

這會(huì)有鎖過(guò)期和釋放其他服務(wù)鎖這種嚴(yán)重的問(wèn)題。

鎖過(guò)期處理

那么鎖過(guò)期這種問(wèn)題該如何處理的?

雖然可以通過(guò)增加刪除key時(shí)間來(lái)處理這個(gè)問(wèn)題,但是并沒(méi)有從根本上解決。假設(shè)設(shè)個(gè)100s,絕大多數(shù)都是1s后就會(huì)釋放鎖,但是由于服務(wù)宕機(jī),則會(huì)導(dǎo)致100s內(nèi)其他服務(wù)都無(wú)法獲取到鎖,這也是災(zāi)難性的。

我們可以這樣做,在鎖將要過(guò)期的時(shí)候,如果服務(wù)還沒(méi)有處理完業(yè)務(wù),那么將這個(gè)鎖再續(xù)一段時(shí)間。比如設(shè)置key在10s后過(guò)期,那么再開(kāi)啟一個(gè)守護(hù)線程,在第8s的時(shí)候檢測(cè)服務(wù)是否處理完,如果沒(méi)有,則將這個(gè)key再續(xù)10s后過(guò)期。

在Redisson(Redis SDK客戶端)中,就已經(jīng)幫我們實(shí)現(xiàn)了這個(gè)功能,這個(gè)自動(dòng)續(xù)時(shí)的我們稱(chēng)其為”看門(mén)狗”。

釋放其他服務(wù)的鎖如何處理呢?

每個(gè)服務(wù)在設(shè)置value的時(shí)候,帶上自己服務(wù)的唯一標(biāo)識(shí),如UUID,或者一些業(yè)務(wù)上的獨(dú)特標(biāo)識(shí)。這樣在刪除key的時(shí)候,只刪除自己服務(wù)之前添加的key就可以了。

如果需要先查看鎖是否是自己服務(wù)添加的,需要先get取出來(lái)判斷,然后再進(jìn)行del。這樣的話就無(wú)法保證原子性了。

我們可以通過(guò)Lua腳本,將這兩個(gè)操作合并成一個(gè)操作,就可以保證其原子性了。

Lua腳本的話,我也不會(huì),用到的時(shí)候百度就完了。

如果是在單redis實(shí)例的情況下,上面的已經(jīng)完全實(shí)現(xiàn)了分布式鎖的功能了。

那么redis宕機(jī)了呢?

這個(gè)時(shí)候就得引入redis集群了。

但是涉及到redis集群,就會(huì)有新的問(wèn)題出現(xiàn),假設(shè)是主從集群,且主從數(shù)據(jù)并不是強(qiáng)一致性。當(dāng)主節(jié)點(diǎn)宕機(jī)后,主節(jié)點(diǎn)的數(shù)據(jù)還未來(lái)得及同步到從節(jié)點(diǎn),進(jìn)行主從切換后,新的主節(jié)點(diǎn)并沒(méi)有老的主節(jié)點(diǎn)的全部數(shù)據(jù),這就會(huì)導(dǎo)致剛寫(xiě)入到老的主節(jié)點(diǎn)的鎖在新的主節(jié)點(diǎn)并沒(méi)有,其他服務(wù)來(lái)獲取鎖時(shí)還是會(huì)加鎖成功。此時(shí)則會(huì)有2個(gè)服務(wù)都可以操作公共資源,此時(shí)的分布式鎖則是不安全的。

redis的作者也想到這個(gè)問(wèn)題,于是他發(fā)明了RedLock。

四、RedLock

什么是RedLock?

要實(shí)現(xiàn)RedLock,需要至少5個(gè)實(shí)例(官方推薦),且每個(gè)實(shí)例都是master,不需要從庫(kù)和哨兵。

實(shí)現(xiàn)流程

1、客戶端先獲取當(dāng)前時(shí)間戳T1

2、客戶端依次向5個(gè)master實(shí)例發(fā)起加鎖命令,且每個(gè)請(qǐng)求都會(huì)設(shè)置超時(shí)時(shí)間(毫秒級(jí),注意:不是鎖的超時(shí)時(shí)間),如果某一個(gè)master實(shí)例由于網(wǎng)絡(luò)等原因?qū)е录渔i失敗,則立即想下一個(gè)master實(shí)例申請(qǐng)加鎖。

3、當(dāng)客戶端加鎖成功的請(qǐng)求大于等于3個(gè)時(shí),且再次獲取當(dāng)前時(shí)間戳T2,

當(dāng)時(shí)間戳T2 - 時(shí)間戳T1 < 鎖的過(guò)期時(shí)間。則客戶端加鎖成功,否則失敗。

4、加鎖成功,開(kāi)始操作公共資源,進(jìn)行后續(xù)業(yè)務(wù)操作

5、加鎖失敗,向所有redis節(jié)點(diǎn)發(fā)送鎖釋放命令

即當(dāng)客戶端在大多數(shù)redis實(shí)例上申請(qǐng)加鎖成功后,且加鎖總耗時(shí)小于鎖過(guò)期時(shí)間,則認(rèn)為加鎖成功。 

 釋放鎖需要向全部節(jié)點(diǎn)發(fā)送鎖釋放命令。

第3步為啥要計(jì)算申請(qǐng)鎖前后的總耗時(shí)與鎖釋放時(shí)間進(jìn)行對(duì)比呢?

因?yàn)槿绻暾?qǐng)鎖的總耗時(shí)已經(jīng)超過(guò)了鎖釋放時(shí)間,那么可能前面申請(qǐng)redis的鎖已經(jīng)被釋放掉了,保證不了大于等于3個(gè)實(shí)例都有鎖存在了,鎖也就沒(méi)有意義了

這樣的話分布式鎖就真的沒(méi)問(wèn)題了嘛?

1、得5個(gè)redis實(shí)例,成本大大增加

2、可以通過(guò)上面的流程感受到,這個(gè)RedLock鎖太重了

3、主從切換這種場(chǎng)景絕大多數(shù)的時(shí)候不會(huì)碰到,偶爾碰到的話,保證最終的兜底操作我覺(jué)得也沒(méi)啥問(wèn)題。

4、分布式系統(tǒng)中的NPC問(wèn)題

分布式系統(tǒng)中的NPC問(wèn)題

(可不是游戲里的NPC提問(wèn)哦)

N:Network Delay,網(wǎng)絡(luò)延遲

P:Process Pause,進(jìn)程暫停(GC)

C:Clock Drift,時(shí)鐘漂移

舉個(gè)例子吧:

1、客戶端 1 請(qǐng)求鎖定節(jié)點(diǎn) A、B、C、D、E

2、客戶端 1 的拿到鎖后,進(jìn)入 GC(時(shí)間比較久)

3、所有 Redis 節(jié)點(diǎn)上的鎖都過(guò)期了

4、客戶端 2 獲取到了 A、B、C、D、E 上的鎖

5、客戶端 1 GC 結(jié)束,認(rèn)為成功獲取鎖

6、客戶端 2 也認(rèn)為獲取到了鎖,發(fā)生【沖突】

在第2步已經(jīng)成功獲取到鎖后,由于GC時(shí)間超過(guò)鎖過(guò)期時(shí)間,導(dǎo)致GC完成后其他客戶端也能夠獲取到鎖,此時(shí)2個(gè)客戶端都會(huì)持有鎖。就會(huì)有問(wèn)題。

這個(gè)問(wèn)題無(wú)論是redlock還是zookeeper都會(huì)有這種問(wèn)題。不做業(yè)務(wù)上的兜底操作就沒(méi)得解。

時(shí)鐘漂移問(wèn)題也只能是盡量避免吧。無(wú)法做到根本解決。

個(gè)人思考

用RedLock覺(jué)得性價(jià)比很低。原因如下

1、得額外的多臺(tái)服務(wù)器部署redis,每臺(tái)服務(wù)器可都是錢(qián)啊,而且部署和運(yùn)維的成本也增加了。

2、用RedLock感覺(jué)太重了,效率會(huì)很低,既然用了redis,就是為了提升效率,結(jié)果一個(gè)鎖大大降低了效率

3、如果在集群情況下有鎖丟失的情況,我們業(yè)務(wù)上做好兜底操作就可以了,可以不用上RedLock。

4、畢竟集群情況下主從切換的場(chǎng)景還是極少的,為了極少的情況去浪費(fèi)大量的性能,感覺(jué)劃不來(lái)

5、就算是上了RedLock,也是避免不了NPC問(wèn)題的,還不是得業(yè)務(wù)上做兜底。

聊了這么多的redis實(shí)現(xiàn)分布式鎖。也簡(jiǎn)單了解下zookeeper是如何實(shí)現(xiàn)分布式鎖的吧。

五、基于zookeeper實(shí)現(xiàn)分布式鎖

什么是zookeeper(zk)?

zk是一個(gè)分布式協(xié)調(diào)服務(wù),功能包括:配置維護(hù)、域名服務(wù)、分布式同步、組服務(wù)等。

zk的數(shù)據(jù)結(jié)構(gòu)跟Unix文件系統(tǒng)類(lèi)似。是一顆樹(shù)形結(jié)構(gòu),這里不做詳細(xì)介紹。

zookeeper節(jié)點(diǎn)介紹

zk的節(jié)點(diǎn)稱(chēng)之為znode節(jié)點(diǎn),znode節(jié)點(diǎn)分兩種類(lèi)型:

1、臨時(shí)節(jié)點(diǎn)(Ephemeral):當(dāng)客戶端與服務(wù)器斷開(kāi)連接后,臨時(shí)znode節(jié)點(diǎn)就會(huì)被自動(dòng)刪除

2、持久節(jié)點(diǎn)(Persistent):當(dāng)客戶端與服務(wù)器斷開(kāi)連接后,持久znode節(jié)點(diǎn)不會(huì)被自動(dòng)刪除

znode節(jié)點(diǎn)還有一些特性:

1、節(jié)點(diǎn)有序:在一個(gè)父節(jié)點(diǎn)下創(chuàng)建子節(jié)點(diǎn),zk提供了一個(gè)可選的有序性,創(chuàng)建子節(jié)點(diǎn)時(shí)會(huì)根據(jù)當(dāng)前子節(jié)點(diǎn)數(shù)量給節(jié)點(diǎn)名添加序號(hào)。例:/root下創(chuàng)建/java,生成的節(jié)點(diǎn)名稱(chēng)則為java0001,/root/java0001。

2、臨時(shí)節(jié)點(diǎn):當(dāng)會(huì)話結(jié)束或超時(shí),自動(dòng)刪除節(jié)點(diǎn)

3、事件監(jiān)聽(tīng):當(dāng)節(jié)點(diǎn)有創(chuàng)建,刪除,數(shù)據(jù)修改,子節(jié)點(diǎn)變更的時(shí)候,zk會(huì)通知客戶端的。

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

zookeeper就是通過(guò)臨時(shí)節(jié)點(diǎn)和節(jié)點(diǎn)有序來(lái)實(shí)現(xiàn)分布式鎖的。

1、每個(gè)獲取鎖的線程會(huì)在zk的某一個(gè)目錄下創(chuàng)建一個(gè)臨時(shí)有序的節(jié)點(diǎn)。

2、節(jié)點(diǎn)創(chuàng)建成功后,判斷當(dāng)前線程創(chuàng)建的節(jié)點(diǎn)的序號(hào)是否是最小的。

3、如果序號(hào)是最小的,那么獲取鎖成功。

 4、如果序號(hào)不是最小的,則對(duì)他序號(hào)的前一個(gè)節(jié)點(diǎn)添加事件監(jiān)聽(tīng)。如果前一個(gè)節(jié)點(diǎn)被刪了(鎖被釋放了),那么就會(huì)喚醒當(dāng)前節(jié)點(diǎn),則成功獲取到鎖。

六、zookeepe和redisr兩者的優(yōu)缺

zookeeper

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

1、不用設(shè)置過(guò)期時(shí)間

2、事件監(jiān)聽(tīng)機(jī)制,加鎖失敗后,可以等待鎖釋放

缺點(diǎn):

1、性能不如redis

2、當(dāng)網(wǎng)絡(luò)不穩(wěn)定時(shí),可能會(huì)有多個(gè)節(jié)點(diǎn)同時(shí)獲取鎖問(wèn)題。例:node1由于網(wǎng)絡(luò)波動(dòng),導(dǎo)致zk將其刪除,剛好node2獲取到鎖,那么此時(shí)node1和node2兩者都會(huì)獲取到鎖。

Redis

優(yōu)點(diǎn):性能上比較好,天然的支持高并發(fā)

缺點(diǎn):

1、獲取鎖失敗后,得輪詢的去獲取鎖

2、大多數(shù)情況下redis無(wú)法保證數(shù)據(jù)強(qiáng)一致性

七、那么實(shí)際的工作中,該如何選擇呢?

比如我來(lái)說(shuō),很簡(jiǎn)單,沒(méi)得選,就Redis,為啥?因?yàn)楣緵](méi)有用zk。

具體如何選擇,還是得看公司是否有使用相應(yīng)的中間件。

如果兩種公司都有使用,那就具體的看業(yè)務(wù)場(chǎng)景了,看是基于性能考慮還是其他方面的考慮。

如果用redis的話,個(gè)人覺(jué)得沒(méi)必要上RedLock,感覺(jué)性價(jià)比太低。

但是要注意的是,無(wú)論哪一種,在極端的情況下,都會(huì)有鎖失效或鎖沖突的情況出現(xiàn),因此業(yè)務(wù)上,設(shè)計(jì)上要有兜底的方案,不要造成不必要的損失。

本文中沒(méi)有通過(guò)代碼來(lái)實(shí)現(xiàn)分布式鎖,只是提供了方向和思路,以及要注意的地方。至于具體如何通過(guò)代碼實(shí)現(xiàn),Java的話有Redisson封裝好了大部分功能,使用起來(lái)也比較簡(jiǎn)單,大家可以參考相應(yīng)的文檔即可。

到此這篇關(guān)于Redis實(shí)現(xiàn)分布式鎖詳解的文章就介紹到這了,更多相關(guān)Redis分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis字符串對(duì)象實(shí)用筆記

    Redis字符串對(duì)象實(shí)用筆記

    這篇文章主要給大家介紹了關(guān)于Redis字符串對(duì)象的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 淺談Redis緩存更新策略

    淺談Redis緩存更新策略

    這篇文章主要介紹了Redis緩存更新策略的相關(guān)資料,講解的十分細(xì)致,有需要的小伙伴可以參考下
    2022-08-08
  • 關(guān)于redisson緩存序列化的幾枚大坑說(shuō)明

    關(guān)于redisson緩存序列化的幾枚大坑說(shuō)明

    這篇文章主要介紹了redisson緩存序列化幾枚大坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Redis官方ORM框架比RedisTemplate更優(yōu)雅

    Redis官方ORM框架比RedisTemplate更優(yōu)雅

    這篇文章主要為大家介紹了Redis官方ORM框架比RedisTemplate更優(yōu)雅的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 一文教你學(xué)會(huì)Redis的事務(wù)

    一文教你學(xué)會(huì)Redis的事務(wù)

    Redis?作為內(nèi)存的存儲(chǔ)中間件,已經(jīng)是面試的面試題必問(wèn)之一了。今天小編就來(lái)和大家一起來(lái)聊聊Redis的事務(wù)吧,希望對(duì)大家有所幫助
    2022-08-08
  • Redis去重的3種不同方法匯總

    Redis去重的3種不同方法匯總

    Redis是完全開(kāi)源免費(fèi)的,遵守BSD協(xié)議,是一個(gè)高性能的key-value數(shù)據(jù)庫(kù),下面這篇文章主要給大家介紹了關(guān)于Redis去重的3種不同方法,需要的朋友可以參考下
    2021-11-11
  • 手把手教你使用redis實(shí)現(xiàn)排行榜功能

    手把手教你使用redis實(shí)現(xiàn)排行榜功能

    使用Redis中有序集合的特性來(lái)實(shí)現(xiàn)排行榜是又好又快的選擇,一般排行榜都是有實(shí)效性的,比如“用戶積分榜”,下面這篇文章主要給大家介紹了關(guān)于使用redis實(shí)現(xiàn)排行榜功能的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • 使用Redis獲取數(shù)據(jù)轉(zhuǎn)json,解決動(dòng)態(tài)泛型傳參的問(wèn)題

    使用Redis獲取數(shù)據(jù)轉(zhuǎn)json,解決動(dòng)態(tài)泛型傳參的問(wèn)題

    這篇文章主要介紹了使用Redis獲取數(shù)據(jù)轉(zhuǎn)json,解決動(dòng)態(tài)泛型傳參的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-07-07
  • Redis獲取某個(gè)大key值的腳本實(shí)例

    Redis獲取某個(gè)大key值的腳本實(shí)例

    這篇文章主要給大家分享介紹了關(guān)于Redis獲取某個(gè)大key值的一個(gè)腳本實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • 深入淺析Redis 集群伸縮原理

    深入淺析Redis 集群伸縮原理

    Redis 集群提供了靈活的節(jié)點(diǎn)擴(kuò)容和收縮方案。在不影響集群對(duì)外服務(wù)的情況下,可以為集群添加節(jié)點(diǎn)進(jìn)行擴(kuò)容,也可以下線部分節(jié)點(diǎn)進(jìn)行縮容,接下來(lái)通過(guò)本文給大家分享Redis 集群伸縮原理,感興趣的朋友一起看看吧
    2021-05-05

最新評(píng)論