redis有一系列的命令,特點是以nx結尾,nx是not exists的縮寫,如setnx命令就應該理解為:set if not exists。這系列的命令非常有用,這裡講使用setnx來實現分布式鎖。
用setnx實現分布式鎖
利用setnx非常簡單地實現分布式鎖。例如:某客戶端要獲得乙個名字foo的鎖,客戶端使用下面的命令進行獲取:
setnx lock.foo
解決死鎖
上面的鎖定邏輯有乙個問題:如果乙個持有鎖的客戶端失敗或崩潰了不能釋放鎖,該怎麼解決?我們可以通過鎖的鍵對應的時間戳來判斷這種情況是否發生了,如果當前的時間已經大於lock.foo的值,說明該鎖已失效,可以被重新使用。
發生這種情況時,可不能簡單的通過del來刪除鎖,然後再setnx一次,當多個客戶端檢測到鎖超時後都會嘗試去釋放它,這裡就可能出現乙個競態條件,讓我們模擬一下這個場景:
c0操作超時了,但它還持有著鎖,c1和c2讀取lock.foo檢查時間戳,先後發現超時了。
c1 傳送del lock.foo
c1 傳送setnx lock.foo 並且成功了。
c2 傳送del lock.foo
c2 傳送setnx lock.foo 並且成功了。
這樣一來,c1,c2都拿到了鎖!問題大了!
幸好這種問題是可以避免的,讓我們來看看c3這個客戶端是怎樣做的:
c3傳送setnx lock.foo 想要獲得鎖,由於c0還持有鎖,所以redis返回給c3乙個0
c3傳送get lock.foo 以檢查鎖是否超時了,如果沒超時,則等待或重試。
反之,如果已超時,c3通過下面的操作來嘗試獲得鎖:
getset lock.foo
通過getset,c3拿到的時間戳如果仍然是超時的,那就說明,c3如願以償拿到鎖了。
如果在c3之前,有個叫c4的客戶端比c3快一步執行了上面的操作,那麼c3拿到的時間戳是個未超時的值,這時,c3沒有如期獲得鎖,需要再次等待或重試。留意一下,儘管c3沒拿到鎖,但它改寫了c4設定的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。
注意:為了讓分布式鎖的演算法更穩鍵些,持有鎖的客戶端在解鎖之前應該再檢查一次自己的鎖是否已經超時,再去做del操作,因為可能客戶端因為某個耗時的操作而掛起,操作完的時候鎖因為超時已經被別人獲得,這時就不必解鎖了。
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不存在。如給定的...