Redis分布式鎖的正確思路及踩坑詳解

2021-10-02 12:19:45 字數 2132 閱讀 5462

一.v1版本

setnx命令可以用於加鎖判斷,對於同乙個key,如果已存在,則未false,不存在則返回true,表示加鎖成功。那麼假設在併發場景下,同一時間假設30個請求打進來,會有29個return返回,只有1個會執行業務**,這裡依靠的是redis的單執行緒模型,不論你的併發,在redis的單執行緒模型裡永遠都會排隊依次執行,同一時間只會執行一條指令。

result := redis.setnx(lockkey,value)

if !result

//業務** 下單邏輯等

redis.delete(localkey)

坑點1:假設業務**拋異常,導致程式奔潰,或者web服務宕機,redis的delete指令不會執行,導致死鎖

解決方案,redis設定超時時間,測試業務**時間,一般超時時間為業務**時間加5s左右.為什麼不加多一點,因為分布式鎖往往應用於高併發場景,例如秒殺,你想想在雙十一如果因為死鎖,而你設定了超時時間會10分鐘,那損失可不是一丁點,對於使用者而言也一點都不友好

二.v2版本

result := redis.setnx(lockkey,value)

if !result

// 加超時時間

redis.expire(lockkey,10s)

//業務** 下單邏輯等

redis.delete(localkey)

坑點2:setnx和expire為兩條指令,非原子性,如果在兩條指令中間系統宕機,同樣死鎖。

你可能會說,redis的速度很快,兩條指令之間的時間可忽略不計。那就大錯特錯了,因為乙個系統有同時肯定有很有請求,萬一在兩條指令之間隔著一條keys*的指令,那可能會消耗十幾秒時間,同樣會有問題。

解決思路,redis的set指令可以同時實現這兩條指令

三,v3版本

result := redis.set(lockkey,value,10s,"nx")

if !result

//業務** 下單邏輯等

redis.delete(localkey)

此版本在一般場景下確實沒有問題,實際上已經滿足大多數中小型公司的需求,然而在真正的高併發場景裡依然存在bug

坑點3:因為你設定的過期時間為10s,在高併發場景下,系統負載公升高,假設業務邏輯執行時間變為11s,那麼10s後,會解鎖,第2個請求則會拿到鎖執行業務邏輯,此時1s後第1個請求會解鎖,解掉第2個請求的鎖,同樣的,第2個請求也會解鎖其他請求的鎖,在真正高併發場景下,這把鎖可能就此永遠失效

解決思路:為每把鎖設定唯一值

四.v4版本

value := uuid.random()

result := redis.set(lockkey,value,10s,"nx")

if !result

//業務** 下單邏輯等

if redis.get("lockkey") == value

你可能已經發現解鎖命令又失去了原子性,與剛才的問題一樣,那怎麼辦呢,redis的delete的命令並沒有此類引數呀,這裡需要用lua指令碼擴充套件

五.v5版本

value := uuid.random()

result := redis.set(lockkey,value,10s,"nx")

if !result

//業務** 下單邏輯等

lua.getanddelete(lockkey,value)

然而還是沒有解決業務邏輯為11s時,超時時間為10s,那麼鎖將提前失效.

解決方案:在加鎖同時,起乙個子執行緒定時掃瞄,用ttl命令獲取lockkey的剩餘時間,如果大於0,則往上再加一定時間,不如說三分之一的超時時間。

程式寫到這裡,大部分場景已經沒有問題。然而如果redis是集群環境,而當你的master結點宕機的時候,從機失去這把鎖,導致超賣等現象的產生。針對這問題,redis的作者提供了一種思路叫紅鎖,redlock,即例如設定5把鎖,能拿到一半以上的鎖時即為成功,需注意的是5把鎖的lockkey一定要不同,這樣在集群環境,5把鎖才會分片到不同的節點,如果key相同,則會分片到同一節點,那麼將毫無意義。

細心的同學可能會發現,最終實現的版本即為redission框架,以上即為此框架的設計思路。

redis實現分布式鎖思路

redis 處理socket ready 事件是單執行緒執行的,也就是說redis一次只會處理乙個socket,避免了併發所帶來的資源競爭問題,可以用來實現分布式鎖.具體實現思路 通過setnx嘗試設值 currenttimestamp timeout 如果設定成功,說明類似獲取鎖操作成功,失敗則獲...

redis實現分布式鎖思路

1.利用redis的setnx命令 setnx name value 如果對應的name沒有被設定過,則會設定成功,如果設定過,則返回失敗。2.expire命令 可以對name設定過期時間,防止持有鎖的執行緒因為意外情況掛掉,導致業務阻塞。3.redis是單執行緒的機制。ps 第二條中expire命...

正確方式實現Redis分布式鎖

實現分布式鎖的方式有很多 1 zookeeper 2 redis 3 阿里開源等。但是在redis中進行實現鎖的網上百分之99的案例中,都如下 long i jedis.setnx key,key if i 1 else 這個是這裡在setnx之後還沒來得及設定過期時間就宕機了,這樣會導致deadl...