還不知道 Redis 分佈式鎖的背後原理?還不趕快學習一下

以前在學校做小項目的時候,用到Redis,基本也只是用來當作緩存。可阿粉在工作中發現,Redis在生產中並不只是當作緩存這麼簡單。在阿粉接觸到的項目中,Redis起到了一個分佈式鎖的作用,具體情況是這樣的:

該項目在某金融平臺中負責某塊業務,是一個分佈式系統,線上大概跑著10個左右的實例。其中有一個步驟需要用戶支付一定的費用,Redis分佈式鎖在其中大概處於這麼一個位置:

還不知道 Redis 分佈式鎖的背後原理?還不趕快學習一下

可以看到在上分佈式鎖之後,系統做了兩個查詢校驗,然後向數據庫中插入了一條訂單記錄,接著才解鎖進入支付流程。

從業務的角度考慮分佈式鎖是好理解的,它保證了查詢及插入數據整個流程的原子性,防止查詢校驗的時候查到髒數據,使得支付前訂單信息落表的操作串行化執行。

儘管從業務上來說很好理解,但使用Redis作為分佈式鎖對我來說是個新知識,阿粉打算結合項目中的代碼,深挖一下這個知識點。

還不知道 Redis 分佈式鎖的背後原理?還不趕快學習一下

正文

1. 為什麼要使用分佈式鎖

在實際項目中見過分佈式鎖後,就不難理解為什麼要使用分佈式鎖了:總結來說就是分佈式系統要訪問共享資源,為了避免併發訪問資源帶來錯誤,我們為共享資源添加一把鎖,讓各個訪問互斥,保證併發訪問的安全性,這就是使用分佈式鎖的原因。

2. Redis中分佈式鎖的實現

redis中使用分佈式鎖很簡單,只要使用setnx指令對某個key上鎖就行:

<code>setnx lock test //上鎖
del lock test //解鎖/<code>

當某個key沒有被佔用的時候,setnx指令會返回1,否則返回0,這就是Redis中分佈式鎖的使用原理。

當然我們還可以在上鎖之後使用expire指令給鎖設置過期時間。

看到這裡你可能會有疑問,如果我們的程序流程不使用指令解鎖,靠redis設置時間過期來解鎖,貌似會出問題。假如我們的服務進程在執行setnx之後和執行expire指令之前掛掉了,那這個鎖豈不是永遠都不能被釋放?

還不知道 Redis 分佈式鎖的背後原理?還不趕快學習一下

沒錯,這確實是個問題,當時人們在Redis的開源社區提出了一堆解決方案專門來解決這個問題,可實現方式都極為複雜。後來Redis的作者在Redis 2.8版本中加入了set指令的擴展參數,使得setnx指令和expire指令能夠同時執行,具體使用像下面這個樣子:

<code>set lock test ex 5 nx
ex:設置鍵的過期時間
nx:只在鍵不存在時,才對鍵進行設置操作/<code>

從此以後,Redis成為了分佈式鎖的寵兒。

3. 分佈式鎖在Redis集群中遇到的麻煩

在學習了Redis中分佈式鎖的使用後,很快我們便發現了新的問題。在企業中,Redis基本上都是集群部署的,集群部署避免不了要面對某個節點宕機的問題。

我們考慮這麼一種情況:假設我們在redis的主節點上添加了一把分佈式鎖,不幸的是主節點掛掉了,而且主節點上的鎖還沒有同步到從節點上,如果此時有客戶端來請求獲得同一把鎖,那麼它將順利地獲得鎖,之前那把鎖會被無情地忽視掉,這就是分佈式鎖在Redis集群中遇到的麻煩。

還不知道 Redis 分佈式鎖的背後原理?還不趕快學習一下

Redis的作者為了解決這個問題提出了一個叫Redlock的算法,它的原理是這樣的:當上鎖的時候,把set指令發送給過半的節點,只要過半的鎖set成功,就認為這次加鎖成功;當解鎖的時候,會向所有的節點發送del指令。

從這個算法的原理可以看出,由於Redlock需要同時對多個節點進行讀寫,因此使用Redlock加分佈式鎖的性能要比單機Redis低很多。因為主從複製出紕漏的概率極低,所以如果對分佈式加鎖過程有一定的容錯率的話,可以考慮直接使用set指令;如果追求高可用性,可以考慮使用Redlock算法。

當然,高可用性的分佈式鎖不只有Redis的Redlock,我們還可以用zookeeper或者支持事務的數據庫做分佈式鎖。

簡述zookeeper的分佈式鎖原理:假設zk用某個節點作為分佈式鎖,當不同的客戶端到zk競爭這把鎖的時候,zk會按順序給不同的客戶端創建一個子節點,掛在作為分佈式鎖的節點下面。假設第一個來到的客戶端為A,第二個來到的是B,分佈式節點下掛的第一個節點就是A,B緊跟著A,且B會監聽著A的生命狀態,當A釋放鎖後A會被刪除,這時B監聽到A被刪除,B接能上位獲得分佈式鎖了。

在公司的項目中,雖然Redis是以集群的方式部署的,但還是使用最基本的set指令獲取分佈式鎖,因為這種方式的性能遠遠高於Redlock算法,也高於zk,數據庫等分佈式鎖實現方式。

雖然在高性能與低概率的錯誤中選擇了高性能,但項目中還是做了其他工作對錯誤情況進行兜底的,比如在公司的項目中對主從複製時的錯誤情況會拋出異常,然後根據異常會進行一些重試的操作。

總結

這次對Redis分佈式鎖的探索算是加深了自己對Redis的理解,但我知道Redis的用處還遠遠不止分佈式鎖和緩存,留著後面繼續探索吧。

還不知道 Redis 分佈式鎖的背後原理?還不趕快學習一下


分享到:


相關文章: