Redisson實現分散式鎖(1)---原理
有關Redisson作為實現分散式鎖,總的分3大模組來講。
1、Redisson實現分散式鎖原理
2、Redisson實現分散式鎖的原始碼解析
3、Redisson實現分散式鎖的專案程式碼(可以用於實際專案中)
本文只介紹Redisson如何實現分散式鎖的原理。其它的會在接下來的部落格講,最後有關Redisson實現分散式鎖的專案程式碼
的部落格中會放上專案原始碼到GitHub上。
一、高效分散式鎖
當我們在設計分散式鎖的時候,我們應該考慮分散式鎖至少要滿足的一些條件,同時考慮如何高效的設計分散式鎖,這裡我認為以下幾點是必須要考慮的。
1、互斥
在分散式高併發的條件下,我們最需要保證,同一時刻只能有一個執行緒獲得鎖,這是最基本的一點。
2、防止死鎖
在分散式高併發的條件下,比如有個執行緒獲得鎖的同時,還沒有來得及去釋放鎖,就因為系統故障或者其它原因使它無法執行釋放鎖的命令,導致其它執行緒都無法獲得鎖,造成死鎖。
所以分散式非常有必要設定鎖的有效時間
,確保系統出現故障後,在一定時間內能夠主動去釋放鎖,避免造成死鎖的情況。
3、效能
對於訪問量大的共享資源,需要考慮減少鎖等待的時間,避免導致大量執行緒阻塞。
所以在鎖的設計時,需要考慮兩點。
1、鎖的顆粒度要儘量小
。比如你要通過鎖來減庫存,那這個鎖的名稱你可以設定成是商品的ID,而不是任取名稱。這樣這個鎖只對當前商品有效,鎖的顆粒度小。
2、鎖的範圍儘量要小
。比如只要鎖2行程式碼就可以解決問題的,那就不要去鎖10行程式碼了。
4、重入
我們知道ReentrantLock是可重入鎖,那它的特點就是:同一個執行緒可以重複拿到同一個資源的鎖。重入鎖非常有利於資源的高效利用。關於這點之後會做演示。
針對以上Redisson都能很好的滿足,下面就來分析下它。
二、Redisson原理分析
為了更好的理解分散式鎖的原理,我這邊自己畫張圖通過這張圖來分析。
1、加鎖機制
執行緒去獲取鎖,獲取成功: 執行lua指令碼,儲存資料到redis資料庫。
執行緒去獲取鎖,獲取失敗: 一直通過while迴圈嘗試獲取鎖,獲取成功後,執行lua指令碼,儲存資料到redis資料庫。
2、watch dog自動延期機制
這個比較難理解,找了些許資料感覺也並沒有解釋的很清楚。這裡我自己的理解就是:
在一個分散式環境下,假如一個執行緒獲得鎖後,突然伺服器宕機了,那麼這個時候在一定時間後這個鎖會自動釋放,你也可以設定鎖的有效時間(不設定預設30秒),這樣的目的主要是防止死鎖的發生。
但在實際開發中會有下面一種情況:
//設定鎖1秒過去
redissonLock.lock("redisson", 1);
/**
* 業務邏輯需要諮詢2秒
*/
redissonLock.release("redisson");
/**
* 執行緒1 進來獲得鎖後,執行緒一切正常並沒有宕機,但它的業務邏輯需要執行2秒,這就會有個問題,在 執行緒1 執行1秒後,這個鎖就自動過期了,
* 那麼這個時候 執行緒2 進來了。那麼就存在 執行緒1和執行緒2 同時在這段業務邏輯裡執行程式碼,這當然是不合理的。
* 而且如果是這種情況,那麼在解鎖時系統會拋異常,因為解鎖和加鎖已經不是同一執行緒了,具體後面程式碼演示。
*/
所以這個時候看門狗
就出現了,它的作用就是 執行緒1 業務還沒有執行完,時間就過了,執行緒1 還想持有鎖的話,就會啟動一個watch dog後臺執行緒,不斷的延長鎖key的生存時間。
注意
正常這個看門狗執行緒是不啟動的,還有就是這個看門狗啟動後對整體效能也會有一定影響,所以不建議開啟看門狗。
3、為啥要用lua指令碼呢?
這個不用多說,主要是如果你的業務邏輯複雜的話,通過封裝在lua指令碼中傳送給redis,而且redis是單執行緒的,這樣就保證這段複雜業務邏輯執行的原子性。
4、可重入加鎖機制
Redisson可以實現可重入加鎖機制的原因,我覺得跟兩點有關:
1、Redis儲存鎖的資料型別是 Hash型別
2、Hash資料型別的key值包含了當前執行緒資訊。
下面是redis儲存的資料
這裡表面資料型別是Hash型別,Hash型別相當於我們java的<key,<key1,value>>
型別,這裡key是指 'redisson'
它的有效期還有9秒,我們再來看裡們的key1值為078e44a3-5f95-4e24-b6aa-80684655a15a:45
它的組成是:
guid + 當前執行緒的ID。後面的value是就和可重入加鎖有關。
舉圖說明
上面這圖的意思就是可重入鎖的機制,它最大的優點就是相同執行緒不需要在等待鎖,而是可以直接進行相應操作。
5、Redis分散式鎖的缺點
Redis分散式鎖會有個缺陷,就是在Redis哨兵模式下:
客戶端1
對某個master節點
寫入了redisson鎖,此時會非同步複製給對應的 slave節點。但是這個過程中一旦發生 master節點宕機,主備切換,slave節點從變為了 master節點。
這時客戶端2
來嘗試加鎖的時候,在新的master節點上也能加鎖,此時就會導致多個客戶端對同一個分散式鎖完成了加鎖。
這時系統在業務語義上一定會出現問題,導致各種髒資料的產生。
缺陷
在哨兵模式或者主從模式下,如果 master例項宕機的時候,可能導致多個客戶端同時完成加鎖。
說明
這篇部落格主要是根據自己的開發經驗,同時也在網上找了許多資料後整理的,如果哪裡有寫的不對,希望多多指點。萬分感激!