用Redis實現(xiàn)分布式鎖一般是用 setnx (set if not exist)
來實現(xiàn),如果可以設(shè)置成功,表示拿到鎖怎茫,用完之后再用del
來釋放石景。
setnx lock true
OK
... do something critical ...
del lock
(integer) 1
但是如果邏輯執(zhí)行到中間出現(xiàn)異常了淋纲,可能會導(dǎo)致 del 指令沒有被調(diào)用增热,這樣就會陷入死鎖堪澎,鎖永遠得不到釋放麦萤。
一般會在拿到鎖之后給鎖設(shè)置一個超時時間炼吴,如60s本鸣,這樣即使出現(xiàn)異常,在60s后鎖還是會被釋放硅蹦。
setnx lock true
OK
expire lock 60
... do something critical ...
del lock
(integer) 1
如果在 setnx 和 expire 之間發(fā)生異常荣德,會導(dǎo)致 expire 得不到執(zhí)行,也會造成死鎖童芹。
最主要原因就是setnx和expire不是原子操作涮瞻,在Redis 2.8 版本中作者加入了 set 指令的擴展參數(shù),使得setnx和expire可以一起執(zhí)行假褪。
set lock true ex 5 nx
OK
... do something critical ...
del lock
多參數(shù)的set命令語法如下:
SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]
-
EX seconds
? 設(shè)置指定的到期時間(以秒為單位)署咽。 -
PX milliseconds
- 設(shè)置指定的到期時間(以毫秒為單位)。 -
NX
- 僅在鍵不存在時設(shè)置鍵生音。 -
XX
- 只有在鍵已存在時才設(shè)置宁否。
Redis分布式鎖不適合執(zhí)行時間很長的任務(wù),因為如果執(zhí)行時間比設(shè)定的失效時間長缀遍,就可能會造成同時有多個線程同時執(zhí)行這一段業(yè)務(wù)邏輯慕匠。
有一個稍微安全一點的解決方案域醇,就是在set的時候?qū)alue設(shè)置為一個隨機數(shù)uuid台谊,在線程執(zhí)行完任務(wù)釋放鎖的時候,要判斷這個uuid是否跟上鎖時候一致譬挚,如果一致則可以釋放殴瘦。
以上做法可以保證一個線程上的鎖不會被另外一個線程釋放(當然姨蟋,還需要保證匹配value的值和釋放鎖是一個原子操作才可以)悠砚,但是如果鎖是過期了自動釋放描融,另外一個線程還是可以重新獲得鎖從而可以執(zhí)行同一段任務(wù)。
參考資料:
[1]Redis深度歷險 https://juejin.im