在之前, 我也使用redis做過分布式鎖, 當時的做法是這樣的:
setnx: 向 redis中建立乙個過期時間為1s的key, 若建立失敗, 則鎖獲取失敗
expire: 獲取鎖成功後, 給鎖增加過期時間
del: 處理後釋放鎖
當時覺得貌似沒什麼問題. 是我太天真了, 今天突然想到, 恩, 有問題.
問題
1.如果在第一步之後, 程式崩了, 沒有給鎖設定過期時間, 導致所有後續操作都無法正常獲取到鎖. 怎麼破?
2.在a成功上鎖後, 因為io阻塞等原因, 執行時間有點長, 鎖已經過期了, 這時b過來成功上鎖, a在釋放鎖的時候釋放的就是b的鎖.
3.redis突然掛了. 如果redis突然掛了, 怎麼辦? 當然, 可以增加redis節點, 主節點掛了, 從節點立刻補上. 但是, 主節點的資料同步到從節點也是需要時間的吧. 假設乙個場景:
a在主節點設定鎖
主節點還沒有同步資料的時候, 掛了
從節點接替成為主節點
b在主節點也成功設定了鎖
這個時候, 分布式鎖就失效了.
那麼有沒有辦法解決上面的問題呢? 我到萬能的谷歌上找了一下, 恩, 真的有.
上面的問題乙個乙個解決.
問題一
如何避免沒有給鎖設定過期時間的問題?
其實看看就知道了, 問題出在設定key和設定value分成兩條命令執行, 所以導致如果在setnx
命令執行過後, 程式崩潰,expire
命令沒有正常執行, 將其合併為一條命令就好啦.
set key value nx px 5000
其中nx
表示存在則不設定,px
表示過期時間.
如此, 至少可以保證不會出現沒有過期時間的鎖了
問題二
如何避免a釋放了b的鎖.
如何避免釋放了其他人的鎖呢? 換個問題, 如何保證這個鎖是你加的呢? so easy, 加鎖的時候, 講value值設定成乙個只有我知道的隨機數字, 釋放的時候看看值是不是我的就行了.
如此在釋放的時候需要兩步操作:
獲取redis鎖的值
若值是我的, 釋放鎖
當然, 為了保證釋放鎖操作的原子性, 這兩步操作最好也能合併為一步操作. 那redis如何實現值是否相同的判斷呢?lua指令碼
.
簡單介紹一下
eval "return " 2 key1 key2 ar**1 ar**2
# 看懂了吧, 哈哈
# eval 是redis內建的命令
# 第乙個引數是執行的指令碼邏輯
# 第二個引數表示後面有幾個key
# 第五個引數開始就是附加引數, 在指令碼邏輯中使用的
所以, 指令碼內容如下:
if redis.call("get",keys[1]) == ar**[1] then
return redis.call("del",keys[1])
else
return 0
end
如此, 至少可以保證不會出現a釋放了b鎖的情況了
問題三
如何保證在主節點掛掉的時候, 從節點接替後, 不會重複獲得鎖?
官網上提供了乙個方法, 從多個redis例項同時獲取鎖. 因為我沒看太明白, 之後看懂了在說吧. 過…
其實, 如果不是處理金錢這種不容出錯的業務, 這種小概率事件個人覺得還是可以容忍的.
最終, 在redis單機下實現的分布式鎖操作如下:
# 獲取分布式鎖,過期時間可調
set lock_key random_value nx px 5000
# ...do something
# 釋放分布式鎖
eval
"if redis.call("get",keys[1]) == ar**[1] then return redis.call("del",keys[1]) else return 0 end" 1 lock_key random_value
基於redis的分布式鎖
public class redislock 加鎖 取到鎖加鎖,並返回值用於解鎖 取不到鎖則立即返回 1 param millitimeout 最長鎖定時間,超時後自動刪除鎖,避免死鎖 return public synchronized long lock long millitimeout lo...
基於 Redis 的分布式鎖
分布式鎖在分布式應用中應用廣泛,想要搞懂乙個新事物首先得了解它的由來,這樣才能更加的理解甚至可以舉一反三。首先談到分布式鎖自然也就聯想到分布式應用。在我們將應用拆分為分布式應用之前的單機系統中,對一些併發場景讀取公共資源時如扣庫存,賣車票之類的需求可以簡單的使用同步或者是加鎖就可以實現。但是應用分布...
基於Redis的分布式鎖
the real target is that i was asked theredis鎖usage in the interview.and i cann t answer it.真正目的是因為在面試中被問到多執行緒這塊怎麼實現的,當時只是看了下 是用redis鎖實現的,至於具體的細節。自己回答的...