Redis 分布式鎖

2021-08-28 08:06:55 字數 1728 閱讀 7731

分布式應用進行邏輯處理時經常會遇到併發問題。

比如乙個操作要修改使用者的狀態,修改狀態需要先讀出使用者的狀態,在記憶體裡進行修改,改完了再存回去。如果這樣的操作同時進行了,就會出現併發問題,因為讀取和儲存狀態這兩個操作不是原子的。(wiki 解釋:所謂原子操作是指不會被執行緒排程機制打斷的操作;這種操作一旦開始,就一直執行到結束,中間不會有任何 context switch 執行緒切換。)這個時候就要使用到分布式鎖來限制程式的併發執行。redis 分布式鎖使用非常廣泛, 分布式鎖本質上要實現的目標就是在 redis 裡面佔乙個「茅坑」,當別的程序也要來佔時,發現已經有人蹲在那裡了,就只好放棄或者稍後再試。

佔坑一般是使用 setnx(set if not exists) 指令,只允許被乙個客戶端佔坑。先來先佔, 用完了,再呼叫 del 指令釋放茅坑。

// 這裡的冒號:就是乙個普通的字元,沒特別含義,它可以是任意其它字元,不要誤解

> setnx lock:codehole true

佔坑完畢,開始那啥

... do something critical ...

佔坑結束。釋放

> del lock:codehole

(integer) 1

但是有個問題,如果邏輯執行到中間出現異常了,可能會導致 del 指令沒有被呼叫,這樣就會陷入死鎖,鎖永遠得不到釋放。

於是我們在拿到鎖之後,再給鎖加上乙個過期時間,比如 5s,這樣即使中間出現異常也可以保證 5 秒之後鎖會自動釋放。

> setnx lock:codehole true

佔坑》 expire lock:codehole 5

佔5秒佔坑後 那啥

... do something critical ...

完畢後,釋放

> del lock:codehole

(integer) 1

但是以上邏輯還有問題。如果在 setnx 和 expire 之間伺服器程序突然掛掉了,可能是因為機器掉電或者是被人為殺掉的,就會導致 expire 得不到執行,也會造成死鎖。這種問題的根源就在於 setnx 和 expire 是兩條指令而不是原子指令。如果這兩條指令可以一起執行就不會出現問題。也許你會想到用 redis 事務來解決。但是這裡不行,因為 expire 是依賴於 setnx 的執行結果的,如果 setnx 沒搶到鎖,expire 是不應該執行的。事務裡沒有 if-else 分支邏輯,事務的特點是一口氣執行,要麼全部執行要麼乙個都不執行。

為了解決這個疑難,redis 開源社群湧現了一堆分布式鎖的 library,專門用來解決這個問題。實現方法極為複雜,小白使用者一般要費很大的精力才可以搞懂。如果你需要使用分布式鎖,意味著你不能僅僅使用 jedis 或者 redis-py 就行了,還得引入分布式鎖的 library。

為了治理這個亂象,redis 2.8 版本中作者加入了 set 指令的擴充套件引數,使得 setnx 和 expire 指令可以一起執行,徹底解決了分布式鎖的亂象。從此以後所有的第三方分布式鎖 library 可以休息了。

> set lock:codehole true ex 5 nx

佔坑5秒

... do something critical ...

佔坑後那啥

完畢釋放

> del lock:codehole

上面這個指令就是 setnx 和 expire 組合在一起的原子指令,它就是分布式鎖的奧義所在。

redis分布式鎖

redis分布式鎖 直接上 我寫了四個redis分布式鎖的方法,大家可以提個意見 第一種方法 redis分布式鎖 param timeout public void lock long timeout thread.sleep 100 catch exception e override publi...

Redis分布式鎖

分布式鎖一般有三種實現方式 1.資料庫樂觀鎖 2.基於redis的分布式鎖 3.基於zookeeper的分布式鎖.首先,為了確保分布式鎖可用,我們至少要確保鎖的實現同時滿足以下四個條件 互斥性。在任意時刻,只有乙個客戶端能持有鎖。不會發生死鎖。即使有乙個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保...

redis分布式鎖

使用redis的setnx命令實現分布式鎖 redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多個客戶端對redis的連線並不存在競爭關係。redis的setnx命令可以方便的實現分布式鎖。setnx key value 將key的值設為value,當且僅當key不存在。如給定的...