5分鐘快速理解redis分布鎖

2023-03-29 22:09:51 來源:騰訊云

打印 放大 縮小

目標(biāo):

我們了解分布式鎖先要理解幾個(gè)問題:

1.任何時(shí)候只有一個(gè)線程持有鎖


(相關(guān)資料圖)

2.要防止一個(gè)線程長期持有鎖甚至是死鎖的情況

3.加鎖和解鎖必須是同一個(gè)進(jìn)程

4.鎖延續(xù)

Redis分布式鎖:

常見的分布式鎖有redis分布式鎖,zookeeper分布式鎖,本文將為大家闡述redis分布式鎖。

首先,redis分布式鎖的本質(zhì)就是在redis占一個(gè)坑位,利用的setnx命令,然后處理完其余的業(yè)務(wù)后再del。再setnx后如果有其它的線程進(jìn)來再setnx那么是set不進(jìn)去的。這就是占坑的原理。

此時(shí)第一個(gè)問題就出現(xiàn)了:在del之前 我的業(yè)務(wù)如果出現(xiàn)了錯(cuò)誤,那么就不會去執(zhí)行del,就會出現(xiàn)死鎖的情況。

這種情況的解決方案很簡單 我們只需要增加一個(gè)超時(shí)時(shí)間即可。比如設(shè)置超時(shí)時(shí)間10s鎖將會自動(dòng)釋放。在redis2.8之后 setnx和expire是原子操作 我們不用考慮setnx后因?yàn)楦鞣N問題沒有expire的情況。

那么現(xiàn)在就會有第二個(gè)問題:鎖超時(shí)問題。

Redisson分布式鎖這邊我們使用redisson的分布式鎖來解決這個(gè)問題。

先看一段lua腳本:

if (redis.call("exists", KEYS[1]) == 0) then " +   "redis.call("hincrby", KEYS[1], ARGV[2], 1); " +   "redis.call("pexpire", KEYS[1], ARGV[1]); " +   "return nil; " +   "end; " +

和大家解釋一下這一段lua腳本的意思:

exsist 先判斷有沒有這個(gè)key,來看鎖是否存在。

存在的話用hincrby設(shè)置一個(gè)hsah結(jié)構(gòu),然后再pexpire設(shè)置過期時(shí)間

我們再看一下redisson的一個(gè)加鎖解鎖流程圖:

我們可以看到redisson使用了 watchdog來做鎖延遲操作。

在我們r(jià)edisson.trylock的時(shí)候有一個(gè)參數(shù)是releasedTime,這個(gè)參數(shù)的含義就是釋放鎖的時(shí)間。我們這個(gè)參數(shù)如果傳了,那么看門狗就會不生效,沒傳的話看門狗生效,這一點(diǎn)很重要。

redisson 看門狗會默認(rèn)10s執(zhí)行一次,如果沒有鎖釋放,那么自動(dòng)鎖延續(xù)。

大家看這張圖可以看到,redisson還采用了redis的消息訂閱與發(fā)布,如果一個(gè)線程設(shè)置了waitTime,他就會去在這個(gè)時(shí)間里去等待,訂閱了一個(gè)channel,當(dāng)占鎖線程一旦釋放了鎖,占鎖線程就回去發(fā)布一條消息,等待的線程訂閱到了 就可以去重試再占鎖。[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-C0EVK9Y0-1678841063259)(redis分布式鎖流程.png)]

流程分析:

1.客戶端1嘗試獲取鎖,返回null則加鎖成功,如果有設(shè)置釋放時(shí)間則直接通過lua腳本去操作redis,如果沒有設(shè)置則開啟看門狗機(jī)制。當(dāng)沒有設(shè)置釋放時(shí)間,默認(rèn)釋放時(shí)間為30s,看門狗機(jī)制會10s進(jìn)行一次所延續(xù)。

2.當(dāng)客戶端2獲取鎖失敗,則通過redis的channel訂閱鎖釋放的時(shí)間。當(dāng)超過最大等待時(shí)間,則鎖失效。如果等待到了鎖釋放時(shí)間的通知,則開始重新進(jìn)入循環(huán)開始重試加鎖。

3.循環(huán)中每次都先試著獲取鎖,并得到已存在鎖的剩余時(shí)間。如果拿到了鎖,直接返回。如果鎖還存在,那么等待釋放鎖的消息,這里采用了信號量來阻塞線程,當(dāng)鎖釋放并發(fā)布釋放鎖的消息后,信號量的release方法被調(diào)用,此時(shí)被信號量阻塞的隊(duì)列中的第一個(gè)線程就可以繼續(xù)嘗試獲取鎖了。

我們再看一下釋放鎖的代碼

// 判斷鎖 key 是否存在            "if (redis.call("hexists", KEYS[1], ARGV[3]) == 0) then " +                "return nil;" +            "end; " +            // 將該客戶端對應(yīng)的鎖的 hash 結(jié)構(gòu)的 value 值遞減為 0 后再進(jìn)行刪除            // 然后再向通道名為 redisson_lock__channel publish 一條 UNLOCK_MESSAGE 信息            "local counter = redis.call("hincrby", KEYS[1], ARGV[3], -1); " +            "if (counter > 0) then " +                "redis.call("pexpire", KEYS[1], ARGV[2]); " +                "return 0; " +            "else " +                "redis.call("del", KEYS[1]); " +                "redis.call("publish", KEYS[2], ARGV[1]); " +                "return 1; "+            "end; " +            "return nil;",Arrays.asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));

步驟解析:

1.判斷是否存在,如果存在的話先把可重入的值遞減為0,再進(jìn)行刪除

2.廣播鎖釋放消息,通知阻塞等待的進(jìn)程(向通道名為redisson_lock__channelpublish 一條 UNLOCK_MESSAGE 信息)。

3.取消看門狗機(jī)制,即將RedissonLock.EXPIRATION_RENEWAL_MAP里面的線程 id 刪除,并且 cancel 掉 Netty 的那個(gè)定時(shí)任務(wù)線程。

總結(jié)

Redisson的優(yōu)點(diǎn):1.通過watchdog解決了 鎖延續(xù)問題

2.和zookeeper比較,性能更高。

3.支持可重入鎖

4.在等待申請鎖資源的進(jìn)程等待申請鎖的實(shí)現(xiàn)上做了優(yōu)化,減少了無效的鎖申請,提高了資源的利用率

缺點(diǎn):1.在redis分布式鎖的情況下,Master redis 加鎖,然后把key同步給slave,此時(shí)master宕機(jī),那么slave變成了master,這就會出現(xiàn)問題,產(chǎn)生臟數(shù)據(jù)。 這里用連鎖的方式可以解決這個(gè)問題。

關(guān)鍵詞:

責(zé)任編輯:ERM523

相關(guān)閱讀

主站蜘蛛池模板: 日韩激情电影在线观看| japanesehd熟女熟妇| 精品久久人人妻人人做精品| 国内精品久久久久影院一蜜桃| 久久精品国产精品亚洲毛片| 精品三级AV无码一区| 国产福利一区二区三区在线观看 | 欧美天天综合色影久久精品| 国产一级毛片视频在线!| 99久久精品费精品国产一区二区 | 人人色在线视频播放| 鲁一鲁中文字幕久久| 天天狠天天透天干天天怕∴| 久久精品国产亚洲AV高清热| 精品三级av无码一区| 国产xxxx做受视频| 51精品国产人成在线观看| 手机在线观看你懂的| 亚洲欧美一区二区三区孕妇| 精品一区二区三区免费毛片爱| 国产三级在线观看a| 国产精品1024永久免费视频| 天堂精品高清1区2区3区| 中文午夜乱理片无码| 樱花草视频www| 免费在线视频a| 香蕉在线视频播放| 国产第一福利136视频导航| 91制片厂制作果冻传媒168| 成人综合在线视频| 亚洲人成色77777| 精品一区精品二区| 四虎影视在线观看永久地址| www视频在线观看| 女人被狂c躁到高潮视频| 久久天天躁狠狠躁夜夜| 欧美白人最猛性xxxxx| 午夜电影在线播放| 国产成人午夜片在线观看| 夜夜添无码试看一区二区三区| 久久一区不卡中文字幕|