1:普通的Redis分布式鎖的缺陷
我們在網上看到的redis分布式鎖的工具方法,大都滿足互斥、防止死鎖的特性,有些工具方法會滿足可重入特性。
如果只滿足上述3種特性會有哪些隱患呢?redis分布式鎖無法自動續期,比如,一個鎖設置了1分鍾超時釋放,如果拿到這個鎖的線程在一分鍾內沒有執行完畢,那么這個鎖就會被其他線程拿到,可能會導致嚴重的線上問題,我已經在秒殺系統故障排查文章中,看到好多因為這個缺陷導致的超賣了。
Redisson 鎖的加鎖機制如上圖所示,線程去獲取鎖,獲取成功則執行lua腳本,保存數據到redis數據庫。
如果獲取失敗: 一直通過while循環嘗試獲取鎖(可自定義等待時間,超時后返回失敗),獲取成功后,執行lua腳本,保存數據到redis數據庫。
Redisson提供的分布式鎖是支持鎖自動續期的,也就是說,如果線程仍舊沒有執行完,那么redisson會自動給redis中的目標key延長超時時間,這在Redisson中稱之為 Watch Dog 機制。
同時 redisson 還有公平鎖、讀寫鎖的實現。
使用樣例如下,附有方法的詳細機制釋義
private void redissonDoc() throws InterruptedException { //1. 普通的可重入鎖 RLock lock = redissonClient.getLock("generalLock"); // 拿鎖失敗時會不停的重試 // 具有Watch Dog 自動延期機制 默認續30s 每隔30/3=10 秒續到30s lock.lock(); // 嘗試拿鎖10s后停止重試,返回false // 具有Watch Dog 自動延期機制 默認續30s boolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 拿鎖失敗時會不停的重試 // 沒有Watch Dog ,10s后自動釋放 lock.lock(10, TimeUnit.SECONDS); // 嘗試拿鎖100s后停止重試,返回false // 沒有Watch Dog ,10s后自動釋放 boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS); //2. 公平鎖 保證 Redisson 客戶端線程將以其請求的順序獲得鎖 RLock fairLock = redissonClient.getFairLock("fairLock"); //3. 讀寫鎖 沒錯與JDK中ReentrantLock的讀寫鎖效果一樣 RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock"); readWriteLock.readLock().lock(); readWriteLock.writeLock().lock(); }
2.Wath Dog的自動延期機制
如果拿到分布式鎖的節點宕機,且這個鎖正好處於鎖住的狀態時,會出現鎖死的狀態,為了避免這種情況的發生,鎖都會設置一個過期時間。這樣也存在一個問題,加入一個線程拿到了鎖設置了30s超時,在30s后這個線程還沒有執行完畢,鎖超時釋放了,就會導致問題,Redisson給出了自己的答案,就是 watch dog 自動延期機制。
Redisson提供了一個監控鎖的看門狗,它的作用是在Redisson實例被關閉前,不斷的延長鎖的有效期,也就是說,如果一個拿到鎖的線程一直沒有完成邏輯,那么看門狗會幫助線程不斷的延長鎖超時時間,鎖不會因為超時而被釋放。
默認情況下,看門狗的續期時間是30s,也可以通過修改Config.lockWatchdogTimeout來另行指定。
另外Redisson 還提供了可以指定leaseTime參數的加鎖方法來指定加鎖的時間。超過這個時間后鎖便自動解開了,不會延長鎖的有效期。
3.結論
上述源碼讀過來我們可以記住幾個關鍵情報:
1.watch dog 在當前節點存活時每10s給分布式鎖的key續期 30s;
2.watch dog 機制啟動,且代碼中沒有釋放鎖操作時,watch dog 會不斷的給鎖續期;
3.從可2得出,如果程序釋放鎖操作時因為異常沒有被執行,那么鎖無法被釋放,所以釋放鎖操作一定要放到 finally {} 中;