redis 分佈式鎖遇到的坑

用鎖遇到過哪些問題?又是如何解決的?

未關閉資源

由於當前線程 獲取到redis 鎖,處理完業務後未及時釋放鎖,導致其它線程會一直嘗試獲取鎖阻塞,例如:用Jedis客戶端會報如下的錯誤信息

1redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis線程池已經沒有空閒線程來處理客戶端命令。使用原生方法記得關閉!

解決的方法也很簡單,只要我們細心一點,拿到鎖的線程處理完業務及時釋放鎖

B的鎖被A給釋放了

我們知道Redis實現鎖的原理在於 SETNX命令。當 key不存在時將 key的值設爲 value ,返回值爲 1;若給定的 key已經存在,則 SETNX不做任何動作,返回值爲 0 。

SETNX key value

我們來設想一下這個場景:A、B兩個線程來嘗試給key myLock加鎖,A線程先拿到鎖(假如鎖3秒後過期),B線程就在等待嘗試獲取鎖,到這一點毛病沒有。

那如果此時業務邏輯比較耗時,執行時間已經超過redis鎖過期時間,這時A線程的鎖自動釋放(刪除key),B線程檢測到myLock這個key不存在,執行 SETNX命令也拿到了鎖。

但是,此時A線程執行完業務邏輯之後,還是會去釋放鎖(刪除key),這就導致B線程的鎖被A線程給釋放了。

爲避免上邊的情況,一般我們在每個線程加鎖時要帶上自己獨有的value值來標識,只釋放指定value的key,否則就會出現釋放鎖混亂的場景

一般我們可以設置value爲業務前綴_當前線程ID或者uuid,只有當前value相同的纔可以釋放鎖

鎖過期了,業務還沒執行完

redis分佈式鎖過期,而業務邏輯沒執行完的場景,不過,這裏換一種思路想問題,把redis鎖的過期時間再弄長點不就解決了嗎?

那還是有問題,我們可以在加鎖的時候,手動調長redis鎖的過期時間,可這個時間多長合適?業務邏輯的執行時間是不可控的,調的過長又會影響操作性能。

要是redis鎖的過期時間能夠自動續期就好了。

爲了解決這個問題我們使用redis客戶端redisson,redisson很好的解決了redis在分佈式環境下的一些棘手問題,它的宗旨就是讓使用者減少對Redis的關注,將更多精力用在處理業務邏輯上。

redisson對分佈式鎖做了很好封裝,只需調用API即可。

1  RLock lock = redissonClient.getLock("stockLock");

redisson在加鎖成功後,會註冊一個定時任務監聽這個鎖,每隔10秒就去查看這個鎖,如果還持有鎖,就對過期時間進行續期。默認過期時間30秒。這個機制也被叫做:“看門狗”

redis主從複製的坑

redis高可用最常見的方案就是主從複製(master-slave),這種模式也給redis分佈式鎖挖了一坑。

redis cluster集羣環境下,假如現在A客戶端想要加鎖,它會根據路由規則選擇一臺master節點寫入key mylock,在加鎖成功後,master節點會把key異步複製給對應的slave節點。

如果此時redis master節點宕機從節點複製失敗,爲保證集羣可用性,會進行主備切換,slave變爲了redis master。B客戶端在新的master節點上加鎖成功,而A客戶端也以爲自己還是成功加了鎖的。另外如果主從複製延遲同樣也會造成加鎖和解鎖延遲的問題。

此時就會導致同一時間內多個客戶端對一個分佈式鎖完成了加鎖,導致各種髒數據的產生。

至於解決辦法嘛,目前看還沒有什麼根治的方法,只能儘量保證機器的穩定性,減少發生此事件的概率,即便是redis作者也沒有特別完美的解決這個問題,參考我的另一篇:Redlock:Redis集羣分佈式鎖

https://blog.csdn.net/zjcjava/article/details/103025997

畢竟redis是保持的AP而非CP,如果要追求強一致性可以使用zookeeper分佈式鎖

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章