java面經查缺補漏之四十一天(今天學redis實現分佈式鎖)

1.如何實現redis分佈式鎖?

參考:https://www.cnblogs.com/williamjie/p/9395659.html

參考:https://mp.weixin.qq.com/s?__biz=MzIxMjE5MTE1Nw==&mid=2653194065&idx=1&sn=1baa162e40d48ce9b44ea5c4b2c71ad7&chksm=8c99f58bbbee7c9d5b5725da5ee38fe0f89d7a816f3414806785aea0fe5ae766769600d3e982&scene=21#wechat_redirect

加鎖的正確操作:

jedis.set(lockKey, requestId, NX, PX, expireTime);

一共五個參數:

第一個爲key,我們使用key來當鎖,因爲key是唯一的。

第二個爲value,解鎖的時候用來判斷是不是自己的鎖,是自己的就解鎖,不是自己的就不解鎖。

第三個爲NX,當key不存在時,我們進行set操作;若key已經存在,則不做任何操作;

第四個爲PX,意思是我們要給這個key加一個過期的設置,具體時間由第五個參數決定。

第五個爲time,與第四個參數相呼應,代表key的過期時間。

常見錯誤:

(1)使用jedis.setnx()jedis.expire()組合實現加鎖,不具有原子性,如果程序在執行完setnx()之後突然崩潰,導致鎖沒有設置過期時間。那麼將會發生死鎖。

解鎖的正確操作:

String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

這裏是用了一段lua代碼來保證原子性。

常見錯誤:

判斷是否是自己的鎖,是的話就解鎖

if (requestId.equals(jedis.get(lockKey))) { // 若在此時,這把鎖突然不是這個客戶端的,則會誤解鎖 jedis.del(lockKey); }

它是不具備原子性的

2.如何實現zookeeper分佈式鎖?

參考:https://mp.weixin.qq.com/s?__biz=MzIxMjE5MTE1Nw==&mid=2653194140&idx=1&sn=07b65a50798c26ecdc0fc555128ab937&chksm=8c99f546bbee7c50b1642dc971cb1f5e244dce661546e141734797c8c23c6c3ad779dfb57d3b&scene=21#wechat_redirect

首先明確一個概念,臨時順序節點

在創建節點時,Zookeeper根據創建的時間順序給該節點名稱進行編號;當創建節點的客戶端與zookeeper斷開連接後,臨時節點會被刪除。

獲取鎖

 

首先,在Zookeeper當中創建一個持久節點ParentLock。當第一個客戶端想要獲得鎖時,需要在ParentLock這個節點下面創建一個臨時順序節點 Lock1。

 

 

 

 

之後,Client1查找ParentLock下面所有的臨時順序節點並排序,判斷自己所創建的節點Lock1是不是順序最靠前的一個。如果是第一個節點,則成功獲得鎖。

 

 

 

 

這時候,如果再有一個客戶端 Client2 前來獲取鎖,則在ParentLock下載再創建一個臨時順序節點Lock2。

 

 

 

 

Client2查找ParentLock下面所有的臨時順序節點並排序,判斷自己所創建的節點Lock2是不是順序最靠前的一個,結果發現節點Lock2並不是最小的。

 

於是,Client2向排序僅比它靠前的節點Lock1註冊Watcher,用於監聽Lock1節點是否存在。這意味着Client2搶鎖失敗,進入了等待狀態。

 

 

 

這時候,如果又有一個客戶端Client3前來獲取鎖,則在ParentLock下載再創建一個臨時順序節點Lock3。

 

 

 

 

Client3查找ParentLock下面所有的臨時順序節點並排序,判斷自己所創建的節點Lock3是不是順序最靠前的一個,結果同樣發現節點Lock3並不是最小的。

 

於是,Client3向排序僅比它靠前的節點Lock2註冊Watcher,用於監聽Lock2節點是否存在。這意味着Client3同樣搶鎖失敗,進入了等待狀態。

 

 

釋放鎖

 

釋放鎖分爲兩種情況:

 

1.任務完成,客戶端顯示釋放

 

當任務完成時,Client1會顯示調用刪除節點Lock1的指令。

 

 

 

 

2.任務執行過程中,客戶端崩潰

 

獲得鎖的Client1在任務執行過程中,如果Duang的一聲崩潰,則會斷開與Zookeeper服務端的鏈接。根據臨時節點的特性,相關聯的節點Lock1會隨之自動刪除。

 

 

 

 

由於Client2一直監聽着Lock1的存在狀態,當Lock1節點被刪除,Client2會立刻收到通知。這時候Client2會再次查詢ParentLock下面的所有節點,確認自己創建的節點Lock2是不是目前最小的節點。如果是最小,則Client2順理成章獲得了鎖。

 

 

 

 

同理,如果Client2也因爲任務完成或者節點崩潰而刪除了節點Lock2,那麼Client3就會接到通知。

 

 

 

 

最終,Client3成功得到了鎖。

 

 

3.Zookeeper和Redis分佈式鎖的優缺點?

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章