更多精彩內容,請關注微信公衆號:後端技術小屋golang
用redis實現分佈式鎖是一個老生常談的問題了。由於redis單條命令執行的原子性和高性能,當多個客戶端執行setnx(相同key)時,最多隻有一個得到成功。所以在對可用性要求不是特別高的場景下,redis分佈式鎖方案不失爲一個性價比高的實現。redis
setnx lockid random px lock-duration
若是不設置過時時間,考慮以下時序:算法
如上所示,客戶端A搶到鎖了,可是因爲某些異常致使進程尚未來得及釋放鎖就退出了。這樣其餘客戶端setnx的返回永遠是0,即永遠也搶不到鎖。後端
相反,若是設置過時時間,即便客戶端A沒有主動釋放鎖,到了過時時間以後redis也會自動釋放。微信
若是實現爲,dom
setnx lockid expire lockid lock-duration
除非使用lua script, 不然redis沒法支持上述兩個命令的原子性,當第一個命令執行完成後,搶到鎖的客戶端A異常退出了,那麼其餘客戶端將永遠搶到鎖。分佈式
注:redis在2.6.12版本後已經支持setnx命令的TTL參數,這個問題不復存在源碼分析
假設鎖的值爲固定值,考慮以下狀況性能
del lockid
若是鎖的值是隨機值,而且每次成功加鎖時,都記錄該隨機值的話,而且釋放鎖時,判斷鎖的值是否等於記錄值,等於則del, 不等於則跳過。lua
若是不使用lua封裝釋放鎖的邏輯,考慮時序:
get lockid
,發現記錄值和鎖當前值相等,斷定該鎖爲本身所加。del lockid
,客戶端B加的鎖被A釋放而redis執行lua script的原子性能避免上述問題。
若是隻在一個redis節點上搶鎖,若是該節點宕機,將致使全部的客戶端都搶不到鎖,沒法保證服務的高可用。
redlock是一種基於redis的分佈式鎖算法。而redsync是redlock算法的golang實現,其暴露了三個API:加鎖(Lock),解鎖(Unlock),續鎖(Extend)
set lockid value NX PX lock-duration
針對全部redis實例,執行lua腳本。這裏會判斷key對應的value和Mutex在Lock時使用的value值是否一致,只有一致了執行del命令。此舉是爲了保證每一個客戶端不會釋放別的客戶端建立的鎖。
if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end
若是有超過半數實例上的請求返回,則意味着釋放鎖成功。不然斷定失敗。
Extend操做是爲了保證當客戶端業務處理時長超過expire時間時,客戶端可主動延長鎖的過時時間,而無需二次搶鎖。針對全部redis鏈接,執行lua腳本,從新設置過時時間
if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end
半數以上返回成功,則意味着Extend成功
推薦閱讀
更多精彩內容,請掃碼關注微信公衆號:後端技術小屋。若是以爲文章對你有幫助的話,請多多分享、轉發、在看。