Java分佈式鎖的四種實現方式(Redis,Zookeeper,Mysql,Memcached)

1,使用Redis做分佈式鎖:

利用SETNX添加一個鎖,並設置鎖的釋放時間。

問題:

a,某個機器實例的任務執行時長超時了,超過了鎖釋放的時間,會造成其他機器實例獲取到該鎖並執行任務。任務被同時執行。

b,Redis的部署模式:如果是單實例,或者是master-slave模式。 Redis可能會掛(概率很小),或者只是針對master節點加鎖,如果master節點故障,發生master,slave切換,鎖丟失。

 

解決方案:

1,針對問題a

* 如果任務時間超時,可以設置告警,人工進行干預。

* 或者在當前機器實例上,頻繁的去get鎖,如果鎖屬於自己,則延長鎖的釋放時間。

2,針對問題b

* 如果是master-slave模式,在每個節點都建立鎖,如果是Cluster模式,在超過一半的節點上都建立鎖。

 

如果業務對分佈式鎖出錯可以容忍,不是那麼強烈的一致性,那就使用Redis的簡單實現,因爲Redis可以支撐

很高的併發。

比如一些週期性定時任務,例如同步數據的,處理異常情況等等,避免多個實例跑浪費機器資源的。

 

如果業務對分佈式鎖有一定要求,當然不是100%的強一致性,而且併發特別高,可以考試使用

Redisson, 他具有很高的數據一致性,可以達到99.99%,而且性能特別好。

可以使用Redisson的多個Redis實例的分佈式鎖。

Config config = new Config(); 
config.useClusterServers().addNodeAddress("redis://192.168.31.101:7001") 
.addNodeAddress("redis://192.168.31.101:7002") 
.addNodeAddress("redis://192.168.31.101:7003") 
.addNodeAddress("redis://192.168.31.102:7001") 
.addNodeAddress("redis://192.168.31.102:7002") 
.addNodeAddress("redis://192.168.31.102:7003"); 
RedissonClient redisson = Redisson.create(config); 
RLock lock = redisson.getLock("anyLock"); 
lock.lock(); 
lock.unlock();

它的 API 中的 Lock 和 Unlock 即可完成分佈式鎖:

Redisson 中有一個 Watchdog 的概念,翻譯過來就是看門狗,它會在你獲取鎖之後,每隔 10s 幫你把 Key 的超時時間設爲 30s。

Redisson 的“看門狗”邏輯保證了沒有死鎖發生。(如果機器宕機了,看門狗也就沒了。此時就不會延長 Key 的過期時間,到了 30s 之後就會自動過期了,其他線程可以獲取到鎖)

 

2,基於Zookeeper實現分佈式鎖

ZK:提供配置管理,分佈式協同,命名的中心化服務。

ZK的節點類型:持久節點,臨時節點

順序節點:持久順序節點,臨時順序節點

 

分佈式鎖實現原理:爲鎖創建一個持久化節點,例如:/lock

在這個節點下,每個Client創建臨時順序節點,如:/lock/client-001,/lock/client-002,/lock/client-003

臨時節點,如果Client會話結束,或者斷開,ZK自動刪除臨時節點。

Client獲取/lock下所有的子節點列表,判斷當前創建的子節點列表序號是不是最小,如果是,認爲獲取該鎖。

如果不是,監聽比自己小一個的節點。直到獲得節點變更通知後重複檢查節點序號。

 

這裏,爲什麼監聽比自己小一個的節點,而不是最小序號的節點,因爲,如果/lock下有1000個節點的話,

當最小節點有變更通知,ZK需要通知其它999個節點。ZK會發生阻塞,Client也沒有必要同時去爭搶鎖。

 

3,基於Mysql數據庫實現分佈式鎖:

悲觀鎖:select ... for update

首先設置Mysql爲非auto commit 模式,放在一個事務執行。

  1. 第一步查詢鎖定一行數據:select * from table where id = 1 for update 鎖定 id=1 的行。
  2. 第二步:操作和這一行數據關聯的業務,比如:insert, 或者 update 等等。
  3. 第三步:提交事務。id=1 的行解鎖。

當然,在事務中,只有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 同一筆數據時會等待其它事務結束後才執行,一般SELECT … 則不受此影響。

 

4,基於Memcached的分佈式鎖實現,和Redis最簡單的分佈式鎖實現功能效果一致。

Memcached的分佈式完全是依賴客戶端的一致性哈希算法來達到分佈式的存儲,在Memcached服務端,所有的操作都是原子性的。

我們利用add函數,add會添加第一個到達的值,並返回true,後續的添加則都會返回false。

利用這個原理,可以先定義一個 鎖 LockKEY ,add 成功的認爲是得到鎖。並且設置[過期超時] 時間,保證宕機後,也不會死鎖。

 

 

 

 

 

 

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