Redis分布式鎖解鎖案例(五)
相關(guān)視頻參考

1.?解鎖代碼
還是先展示代碼,再帶大家慢慢解釋為什么這樣實現(xiàn):
?
可以看到,我們解鎖只需要兩行代碼就搞定了!第一行代碼,我們寫了一個簡單的Lua腳本代碼,上一次見到這個編程語言還是在《黑客與畫家》里,沒想到這次居然用上了。第二行代碼,我們將Lua代碼傳到j(luò)edis.eval()方法里,并使參數(shù)KEYS[1]賦值為lockKey,ARGV[1]賦值為requestId。eval()方法是將Lua代碼交給Redis服務(wù)端執(zhí)行。
那么這段Lua代碼的功能是什么呢?其實很簡單,首先獲取鎖對應(yīng)的value值,檢查是否與requestId相等,如果相等則刪除鎖(解鎖)。那么為什么要使用Lua語言來實現(xiàn)呢?因為要確保上述操作是原子性的。那么為什么執(zhí)行eval()方法可以確保原子性,源于Redis的特性,下面是官網(wǎng)對eval命令的部分解釋:
?
簡單來說,就是在eval命令執(zhí)行Lua代碼的時候,Lua代碼將被當(dāng)成一個命令去執(zhí)行,并且直到eval命令執(zhí)行完成,Redis才會執(zhí)行其他命令。
2.錯誤示例1
最常見的解鎖代碼就是直接使用jedis.del()方法刪除鎖,這種不先判斷鎖的擁有者而直接解鎖的方式,會導(dǎo)致任何客戶端都可以隨時進行解鎖,即使這把鎖不是它的。
?
3.錯誤示例2
這種解鎖代碼乍一看也是沒問題,甚至我之前也差點這樣實現(xiàn),與正確姿勢差不多,唯一區(qū)別的是分成兩條命令去執(zhí)行,代碼如下:
?
如代碼注釋,問題在于如果調(diào)用jedis.del()方法的時候,這把鎖已經(jīng)不屬于當(dāng)前客戶端的時候會解除他人加的鎖。那么是否真的有這種場景?答案是肯定的,比如客戶端A加鎖,一段時間之后客戶端A解鎖,在執(zhí)行jedis.del()之前,鎖突然過期了,此時客戶端B嘗試加鎖成功,然后客戶端A再執(zhí)行del()方法,則將客戶端B的鎖給解除了。
4.怎么保證鎖時間大于業(yè)務(wù)執(zhí)行時間?
redis里面有一個看門狗的機制,其實可以根據(jù)這個思路來,加鎖成功后,啟動一條守護線程,守護線程給鎖進行無限續(xù)期!當(dāng)鎖不存在的時候就跳過,存在就續(xù)期,可以保證鎖的時間大于業(yè)務(wù)時間!線程為守護線程的原因是,守護線程依賴于主線程,當(dāng)主線程掛了之后,守護線程也會掛掉!這樣能避免程序宕機之后,續(xù)期的線程依舊續(xù)期,造成死鎖!
5總結(jié)
本文主要介紹了如何使用Java代碼正確實現(xiàn)Redis分布式鎖,對于加鎖和解鎖也分別給出了兩個比較經(jīng)典的錯誤示例。其實想要通過Redis實現(xiàn)分布式鎖并不難,只要保證能滿足可靠性里的四個條件?;ヂ?lián)網(wǎng)雖然給我們帶來了方便,只要有問題就可以google,然而網(wǎng)上的答案一定是對的嗎?其實不然,所以我們更應(yīng)該時刻保持著質(zhì)疑精神,多想多驗證。
如果你的項目中Redis是多機部署的,那么可以嘗試使用Redisson實現(xiàn)分布式鎖。