5分鐘快速理解redis分布鎖

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

打印 放大 縮小

目標:

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

1.任何時候只有一個線程持有鎖


(相關資料圖)

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

3.加鎖和解鎖必須是同一個進程

4.鎖延續

Redis分布式鎖:

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

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

此時第一個問題就出現了:在del之前 我的業務如果出現了錯誤,那么就不會去執行del,就會出現死鎖的情況。

這種情況的解決方案很簡單 我們只需要增加一個超時時間即可。比如設置超時時間10s鎖將會自動釋放。在redis2.8之后 setnx和expire是原子操作 我們不用考慮setnx后因為各種問題沒有expire的情況。

那么現在就會有第二個問題:鎖超時問題。

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

先看一段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 先判斷有沒有這個key,來看鎖是否存在。

存在的話用hincrby設置一個hsah結構,然后再pexpire設置過期時間

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

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

在我們redisson.trylock的時候有一個參數是releasedTime,這個參數的含義就是釋放鎖的時間。我們這個參數如果傳了,那么看門狗就會不生效,沒傳的話看門狗生效,這一點很重要。

redisson 看門狗會默認10s執行一次,如果沒有鎖釋放,那么自動鎖延續。

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

流程分析:

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

2.當客戶端2獲取鎖失敗,則通過redis的channel訂閱鎖釋放的時間。當超過最大等待時間,則鎖失效。如果等待到了鎖釋放時間的通知,則開始重新進入循環開始重試加鎖。

3.循環中每次都先試著獲取鎖,并得到已存在鎖的剩余時間。如果拿到了鎖,直接返回。如果鎖還存在,那么等待釋放鎖的消息,這里采用了信號量來阻塞線程,當鎖釋放并發布釋放鎖的消息后,信號量的release方法被調用,此時被信號量阻塞的隊列中的第一個線程就可以繼續嘗試獲取鎖了。

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

// 判斷鎖 key 是否存在            "if (redis.call("hexists", KEYS[1], ARGV[3]) == 0) then " +                "return nil;" +            "end; " +            // 將該客戶端對應的鎖的 hash 結構的 value 值遞減為 0 后再進行刪除            // 然后再向通道名為 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,再進行刪除

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

3.取消看門狗機制,即將RedissonLock.EXPIRATION_RENEWAL_MAP里面的線程 id 刪除,并且 cancel 掉 Netty 的那個定時任務線程。

總結

Redisson的優點:1.通過watchdog解決了 鎖延續問題

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

3.支持可重入鎖

4.在等待申請鎖資源的進程等待申請鎖的實現上做了優化,減少了無效的鎖申請,提高了資源的利用率

缺點:1.在redis分布式鎖的情況下,Master redis 加鎖,然后把key同步給slave,此時master宕機,那么slave變成了master,這就會出現問題,產生臟數據。 這里用連鎖的方式可以解決這個問題。

關鍵詞:

責任編輯:ERM523

相關閱讀

亚洲精品欧美精品,亚洲人成在线影院,亚洲电影欧美电影有声小说,麻豆九一精品爱看视频在线观看免费
>