還不懂分布式鎖?看看 Redisson 是如何實現(xiàn)分布式鎖的!

來源:https://www.cnblogs.com/qdhxhz/p/11046905.html
作者:雨點的名字

Redisson 實現(xiàn)分布式鎖

有關(guān)Redisson作為實現(xiàn)分布式鎖,總的分3大模塊來講。

    1、Redisson實現(xiàn)分布式鎖原理
    2、Redisson實現(xiàn)分布式鎖的源碼解析
    3、Redisson實現(xiàn)分布式鎖的項目代碼(可以用于實際項目中)

本文只介紹Redisson如何實現(xiàn)分布式鎖的原理。

一、高效分布式鎖

當(dāng)我們在設(shè)計分布式鎖的時候,我們應(yīng)該考慮分布式鎖至少要滿足的一些條件,同時考慮如何高效的設(shè)計分布式鎖,這里我認(rèn)為以下幾點是必須要考慮的。

1、互斥

在分布式高并發(fā)的條件下,我們最需要保證,同一時刻只能有一個線程獲得鎖,這是最基本的一點。

2、防止死鎖

在分布式高并發(fā)的條件下,比如有個線程獲得鎖的同時,還沒有來得及去釋放鎖,就因為系統(tǒng)故障或者其它原因使它無法執(zhí)行釋放鎖的命令,導(dǎo)致其它線程都無法獲得鎖,造成死鎖。

所以分布式非常有必要設(shè)置鎖的 有效時間 ,確保系統(tǒng)出現(xiàn)故障后,在一定時間內(nèi)能夠主動去釋放鎖,避免造成死鎖的情況。

3、性能

對于訪問量大的共享資源,需要考慮減少鎖等待的時間,避免導(dǎo)致大量線程阻塞。

所以在鎖的設(shè)計時,需要考慮兩點。

1、 鎖的顆粒度要盡量小
。比如你要通過鎖來減庫存,那這個鎖的名稱你可以設(shè)置成是商品的ID,而不是任取名稱。這樣這個鎖只對當(dāng)前商品有效,鎖的顆粒度小。

2、 鎖的范圍盡量要小 。比如只要鎖2行代碼就可以解決問題的,那就不要去鎖10行代碼了。

4、重入

我們知道ReentrantLock是可重入鎖,那它的特點就是:同一個線程可以重復(fù)拿到同一個資源的鎖。重入鎖非常有利于資源的高效利用。關(guān)于這點之后會做演示。

針對以上Redisson都能很好的滿足,下面就來分析下它。

二、Redisson原理分析

為了更好的理解分布式鎖的原理,我這邊自己畫張圖通過這張圖來分析。

image

1、加鎖機(jī)制

線程去獲取鎖,獲取成功: 執(zhí)行l(wèi)ua腳本,保存數(shù)據(jù)到redis數(shù)據(jù)庫。

線程去獲取鎖,獲取失敗: 一直通過while循環(huán)嘗試獲取鎖,獲取成功后,執(zhí)行l(wèi)ua腳本,保存數(shù)據(jù)到redis數(shù)據(jù)庫。

2、watch dog自動延期機(jī)制

這個比較難理解,找了些許資料感覺也并沒有解釋的很清楚。這里我自己的理解就是:

在一個分布式環(huán)境下,假如一個線程獲得鎖后,突然服務(wù)器宕機(jī)了,那么這個時候在一定時間后這個鎖會自動釋放,你也可以設(shè)置鎖的有效時間(不設(shè)置默認(rèn)30秒),這樣的目的主要是防止死鎖的發(fā)生。

但在實際開發(fā)中會有下面一種情況:

          //設(shè)置鎖1秒過去
            redissonLock.lock("redisson", 1);
            /**
             * 業(yè)務(wù)邏輯需要咨詢2秒
             */
            redissonLock.release("redisson");

          /**
           * 線程1 進(jìn)來獲得鎖后,線程一切正常并沒有宕機(jī),但它的業(yè)務(wù)邏輯需要執(zhí)行2秒,這就會有個問題,在 線程1 執(zhí)行1秒后,這個鎖就自動過期了,
           * 那么這個時候 線程2 進(jìn)來了。那么就存在 線程1和線程2 同時在這段業(yè)務(wù)邏輯里執(zhí)行代碼,這當(dāng)然是不合理的。
           * 而且如果是這種情況,那么在解鎖時系統(tǒng)會拋異常,因為解鎖和加鎖已經(jīng)不是同一線程了,具體后面代碼演示。
           */

所以這個時候 看門狗 就出現(xiàn)了,它的作用就是 線程1 業(yè)務(wù)還沒有執(zhí)行完,時間就過了,線程1 還想持有鎖的話,就會啟動一個watch
dog后臺線程,不斷的延長鎖key的生存時間。

注意 正常這個看門狗線程是不啟動的,還有就是這個看門狗啟動后對整體性能也會有一定影響,所以不建議開啟看門狗。

3、為啥要用lua腳本呢?

這個不用多說,主要是如果你的業(yè)務(wù)邏輯復(fù)雜的話,通過封裝在lua腳本中發(fā)送給redis,而且redis是單線程的,這樣就保證這段復(fù)雜業(yè)務(wù)邏輯執(zhí)行的
原子性 。

4、可重入加鎖機(jī)制

Redisson可以實現(xiàn)可重入加鎖機(jī)制的原因,我覺得跟兩點有關(guān):

    1、Redis存儲鎖的數(shù)據(jù)類型是 Hash類型
    2、Hash數(shù)據(jù)類型的key值包含了當(dāng)前線程信息。

下面是redis存儲的數(shù)據(jù)

image

這里表面數(shù)據(jù)類型是Hash類型,Hash類型相當(dāng)于我們java的 <key,<key1,value>> 類型,這里key是指 'redisson'

它的有效期還有9秒,我們再來看里們的key1值為 078e44a3-5f95-4e24-b6aa-80684655a15a:45 它的組成是:

guid + 當(dāng)前線程的ID。后面的value是就和可重入加鎖有關(guān)。

舉圖說明

image

上面這圖的意思就是可重入鎖的機(jī)制,它最大的優(yōu)點就是相同線程不需要在等待鎖,而是可以直接進(jìn)行相應(yīng)操作。

5、Redis分布式鎖的缺點

Redis分布式鎖會有個缺陷,就是在Redis哨兵模式下:

客戶端1 對某個 master節(jié)點 寫入了redisson鎖,此時會異步復(fù)制給對應(yīng)的 slave節(jié)點。但是這個過程中一旦發(fā)生
master節(jié)點宕機(jī),主備切換,slave節(jié)點從變?yōu)榱?master節(jié)點。

這時 客戶端2 來嘗試加鎖的時候,在新的master節(jié)點上也能加鎖,此時就會導(dǎo)致多個客戶端對同一個分布式鎖完成了加鎖。

這時系統(tǒng)在業(yè)務(wù)語義上一定會出現(xiàn)問題, 導(dǎo)致各種臟數(shù)據(jù)的產(chǎn)生 。

缺陷 在哨兵模式或者主從模式下,如果 master實例宕機(jī)的時候,可能導(dǎo)致多個客戶端同時完成加鎖。

說明 這篇博客主要是根據(jù)自己的開發(fā)經(jīng)驗,同時也在網(wǎng)上找了許多資料后整理的,如果哪里有寫的不對,希望多多指點。萬分感激!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容