當前位置:
首頁 > 最新 > 基於 Redis 的分散式鎖

基於 Redis 的分散式鎖


前言

分散式鎖在分散式應用中應用廣泛,想要搞懂一個新事物首先得了解它的由來,這樣才能更加的理解甚至可以舉一反三。

首先談到分散式鎖自然也就聯想到分散式應用。

但是應用分散式了之後系統由以前的單進程多線程的程序變為了多進程多線程,這時使用以上的解決方案明顯就不夠了。

因此業界常用的解決方案通常是藉助於一個第三方組件並利用它自身的排他性來達到多進程的互斥。如:

基於 DB 的唯一索引。

基於 ZK 的臨時有序節點。

基於 Redis 的 參數。

這裡主要基於 Redis 進行討論。


既然是選用了 Redis,那麼它就得具有排他性才行。同時它最好也有鎖的一些基本特性:

高性能(加、解鎖時高性能)

可以使用阻塞鎖與非阻塞鎖。

不能出現死鎖。

可用性(不能出現節點 down 掉後加鎖失敗)。

這裡利用 時的一個 NX 參數可以保證在這個 key 不存在的情況下寫入成功。並且再加上 EX 參數可以讓該 key 在超時之後自動刪除。

所以利用以上兩個特性可以保證在同一時刻只會有一個進程獲得鎖,並且不會出現死鎖(最壞的情況就是超時自動刪除 key)。


實現代碼如下:

注意這裡使用的 jedis 的

api。

該命令可以保證 NX EX 的原子性。

一定不要把兩個命令(NX EX)分開執行,如果在 NX 之後程序出現問題就有可能產生死鎖。

阻塞鎖

同時也可以實現一個阻塞鎖:


解鎖也很簡單,其實就是把這個 key 刪掉就萬事大吉了,比如使用 命令。

但現實往往沒有那麼 easy。

如果進程 A 獲取了鎖設置了超時時間,但是由於執行周期較長導致到了超時時間之後鎖就自動釋放了。這時進程 B 獲取了該鎖執行很快就釋放鎖。這樣就會出現進程 B 將進程 A 的鎖釋放了。

所以最好的方式是在每次解鎖時都需要判斷鎖是否是自己的。

這時就需要結合加鎖機制一起實現了。

加鎖時需要傳遞一個參數,將該參數作為這個 key 的 value,這樣每次解鎖時判斷 value 是否相等即可。

所以解鎖代碼就不能是簡單的 了。

這裡使用了一個 腳本來判斷 value 是否相等,相等才執行 del 命令。

使用 也可以保證這裡兩個操作的原子性。

因此上文提到的四個基本特性也能滿足了:

使用 Redis 可以保證性能。

阻塞鎖與非阻塞鎖見上文。

利用超時機制解決了死鎖。

Redis 支持集群部署提高了可用性。


我自己有擼了一個完整的實現,並且已經用於了生產,有興趣的朋友可以開箱使用:

maven 依賴:

配置 bean :

使用:

使用很簡單。這裡主要是想利用 Spring 來幫我們管理 RedisLock 這個單例的 bean,所以在釋放鎖的時候需要手動(因為整個上下文只有一個 RedisLock 實例)的傳入 key 以及 request(api 看起來不是特別優雅)。

也可以在每次使用鎖的時候 new 一個 RedisLock 傳入 key 以及 request,這樣倒是在解鎖時很方便。但是需要自行管理 RedisLock 的實例。各有優劣吧。

項目源碼在:

https://github.com/crossoverJie/distributed-lock-redis

歡迎討論。


在做這個項目的時候讓我不得不想提一下單測

因為這個應用是強依賴於第三方組件的(Redis),但是在單測中我們需要排除掉這種依賴。比如其他夥伴 fork 了該項目想在本地跑一遍單測,結果運行不起來:

有可能是 Redis 的 ip、埠和單測里的不一致。

Redis 自身可能也有問題。

也有可能是該同學的環境中並沒有 Redis。

所以最好是要把這些外部不穩定的因素排除掉,單測只測我們寫好的代碼。

於是就可以引入單測利器 了。

它的想法很簡答,就是要把你所依賴的外部資源統統屏蔽掉。如:資料庫、外部介面、外部文件等等。

使用方式也挺簡單,可以參考該項目的單測:

這裡只是簡單演示下,可以的話下次仔細分析分析。

它的原理其實也挺簡單,debug 的話可以很直接的看出來:

這裡我們所依賴的 JedisCluster 其實是一個 。所以也不難想到它是如何工作的。

比如這裡我們需要用到 JedisCluster 的 set 函數並需要它的返回值。

Mock 就將該對象代理了,並在實際執行 set 方法後給你返回了一個你自定義的值。

這樣我們就可以隨心所欲的測試了,完全把外部依賴所屏蔽了


至此一個基於 Redis 的分散式鎖完成,但是依然有些問題。

如在 key 超時之後業務並沒有執行完畢但卻自動釋放鎖了,這樣就會導致並發問題。

就算 Redis 是集群部署的,如果每個節點都只是 master 沒有 slave,那麼 master 宕機時該節點上的所有 key 在那一時刻都相當於是釋放鎖了,這樣也會出現並發問題。就算是有 slave 節點,但如果在數據同步到 salve 之前 master 宕機也是會出現上面的問題。

感興趣的朋友還可以參考 Redisson 的實現。

最近在總結一些 Java 相關的知識點,感興趣的朋友可以一起維護。

地址: https://github.com/crossoverJie/Java-Interview

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 crossoverJie 的精彩文章:

TAG:crossoverJie |