利用redis實現分布式鎖

2021-10-10 14:05:57 字數 2686 閱讀 2173

因為redis是單執行緒程式,可以天然的保證執行緒安全,只要我們的命令是單條命令,就可以保證操作的安全性,而redis中給我們提供了setnx key value命令,setnx命令的作用就是當我們的redis中沒有這個key的鍵值隊時,就會建立這個鍵值隊的值,如果已經有了這個key就不作操作;所以我們可以使用setnx命令來實現我們的分布式加鎖操作。但setnx的命令會存在風險

所以當我們需要給鎖設定過期時間時,redis又給我們提供了另外乙個命令:

set key value [ex seconds | px milliseconds] [nx]

例項:set lock_key unique_value nx px 10000

其中:

為什麼我們的value要保證使用者的唯一性呢?主要就是因為當我們在釋放鎖也就是del key時,需要保證只有真正獲得鎖的程式才能去釋放對應的鎖,而不是誰都可以進行鎖的釋放,防止自己獲得鎖,被別人釋放,因此我們釋放鎖的步驟就變成了:

先去判斷鎖變數的值,是否是自己獲得鎖。

如果是自己獲取的鎖,就可以進行鎖釋放。

如果不是自己獲取的鎖,就無法進行鎖釋放。

所以當我們進行釋放鎖時,就不只是乙個命令了,那麼在redis中為了保證多個命令的原子性,我們就需要編寫lua指令碼去進行鎖的釋放,釋放鎖的指令碼如下:

if redis.call("get",keys[1]) == ar**[1] 

then

return redis.call("del",keys[1])

else

return 0

end

其中:這個指令碼的語義就是當輸入的keys[1]在redis的值等於輸入的ar**[1]時,刪除這個原有的key,即釋放鎖;不等則無法釋放鎖;keys[1]:獲取鎖時輸入的key,即資源名; ar**[1]:即是獲取鎖輸入時的value,這個value的唯一性決定了使用者只能刪除自身已經獲取的鎖,不會誤刪到別人的。

但是基於單個節點的redis鎖無法存在單點故障,並且無法保證鎖的高可用性,就算是主從結構,當主節點發生故障;而從節點還有沒有鎖的資訊值時,我們的鎖也就丟失了;所以為了解決上述問題;redis作者提供了乙個基於多個redis節點實現的鎖,也就是redlock。

redlock的基本思路就是:讓客戶端分別和多個redis例項依次進行加鎖操作,如果客戶端能夠獲取半數以上的服務端的加鎖成功操作,那麼就認為客戶端獲取了分布式鎖。其加鎖步驟可以分為三個步驟:

客戶端獲取當前時間

客戶端按順序依次與redis服務端進行加鎖操作(加鎖操作,就是和單個redis節點一樣使用 set 命令加上選項 nx 和過期時間px/ex,同時為了保證不會單個redis例項宕機導致無法正常獲取分布式鎖,需要給加鎖操作設定乙個超時時間)

戶端完成和所有例項的加鎖操作後,客戶端需要進行計算整個加鎖的總耗時時間。並且需要滿足兩個條件,才算加鎖成功:

超過半數的redis伺服器加鎖成功(大於n/(2 + 1))

加鎖的總耗時小於鎖的過期時間

如果加鎖操作,沒有成功完成,redis客戶端,需要向所有伺服器傳送刪除鎖命令操作。

使用了redlock就能保證我們鎖的安全性了?針對redlock我們可以設定一些場景去看一下存在的問題:

場景一:

假設n=5,a,b,c,d,e

1.使用者1成功鎖住了大多數(假設是a,b,c。沒有成功的是d,e),使用者1獲取鎖成功。

2.大多數中有乙個節點宕機(假設是c節點),並且該節點並沒有持久化使用者1的鎖。

3.節點c重啟了,使用者2這個時候獲取到了大多數(c、d、e)

4.出現了使用者1,使用者2同時獲取到了鎖,對共享資源操作的互斥性被破壞了。

場景二:

假設n=5,a,b,c,d,e

1.使用者1成功鎖住了大多數(假設是a,b,c。沒有成功的是d,e),使用者1獲取鎖成功。

2.大多數中有乙個節點出現了時鐘跳躍(假設是c節點出現了時鐘跳躍到了鎖失效時間之後,導致鎖過期)

3.使用者2這個時候獲取到了大多數(c、d、e)

4.出現了使用者1,使用者2同時獲取到了鎖,對共享資源操作的互斥性被破壞了

場景三:

假設n=5,a,b,c,d,e

1.使用者1成功鎖住了大多數,獲取鎖成功

2.使用者1開始對共享資源進行操作

3.異常發生:使用者1從開始操作共享資源到釋放鎖這段時間已經超過了實際鎖的有效時間,但是使用者1沒有感知

4.異常發生期間使用2獲取到了鎖,並開始對鎖進行操作

5.出現了使用者1和使用者2同時操作共享資源,互斥性被破壞了

上面出現的場景就說明了redlock也並不100%是保證鎖的安全的,一旦發生時鐘跳躍或者獲取鎖的資源因為某些原因導致超過了鎖的有效時間,就會導致redlock失效。

對應redis實現分布式鎖的建議就是:對於不需要保證鎖的強安全性的時候,我們直接使用redis單節點的分布式鎖就可以(如:郵件系統,多傳送乙份重複的郵件並不致命);對於redlock畢竟時基於客戶端的分布式鎖,在實現上有一定的難度,需要客戶端自己協調整個鎖機制;而類似zookeeper,etcd,chubby這些,集群直接通過更嚴格的分布式協議zab,raft,或者paxos內部協調整個鎖資源的一致性。對使用者而言只有乙個操作入口,會更友好些。

利用redis實現分布式鎖

一.對於分布式的應用,一定程度上會增加處理的速度。但是也會帶來一些分布式上的麻煩,比如有個需求 後台程式部署在多台伺服器上,client向該後台程式傳送引數為 使用者賬號和 賬號型別 的rpc請求,後台程式需要返回該賬號對應的身份資訊 邏輯很簡單,先判斷庫中有沒有該賬號資訊,有就返回,沒有就新生成乙...

利用Redis實現分布式鎖

實現 redis 完成分布式鎖 所用到的指令 思路 1 在執行具體的買票業務之前先通過 setnx 的指令去獲取其返回值,如果設定成功 返回值為1 說明獲取到了鎖,沒有設定成功 返回值為0 則說明沒有獲取到鎖,繼續迴圈執行 2.搶到鎖的執行緒先給key 設定過期時間,這一步主要是為了避免死鎖問題 在...

golang 利用redis實現分布式鎖

redis知識準備 redis setnx命令 setnx 如果key不存在則建立,並返回true 如果key存在則不操作,並返回false redis ttl命令 如果對key設定了過期時間,則ttl命令返回key到過期時所剩餘的時間 如果key沒有設定過期時間,則ttl命令返回 1 獲取鎖的過期...