在很多分布式的環境中都要求實現分布式事務、分布式鎖,但是java在單機環境中是無法實現的,必須借助外部工具來實現
1、基於數據庫
2、基於redis或者memcached
3、基於Zookeeper
但是這三種的實現也各有有缺點:
性能:緩存 > Zookeeper >= 數據庫
可靠:Zookeeper > 緩存 > 數據庫
一、基於數據庫來實現分布鎖
原理:
最簡單的就是直接創建一張鎖表,然后通過操作該表中的數據來實現了。
當我們要鎖住某個方法或資源時,我們就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。
1、數據庫的性能以及可靠性都會影響性能,數據庫必須實現高可用性部署,防止主機掛了,整個業務就掛了
2、這把鎖只能是非阻塞的,沒法重入,也無法實現隊列機制。重入必須一直while,直到有最終結果回應
3、加大數據庫的開銷影響其他的業務的進行
二、基於緩存實現分布鎖
這里主講redis
1、選用Redis實現分布式鎖原因:
Redis有很高的性能;
Redis命令對此支持較好,實現起來比較方便
Redis是單線程的,天生就能處理並發
2、使用命令介紹:
(1)SETNX
SETNX key val:當且僅當key不存在時,set一個key為val的字符串,返回1;若key存在,則什么都不做,返回0。
(2)expire
expire key timeout:為key設置一個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。
(3)delete
delete key:刪除key
在使用Redis實現分布式鎖的時候,主要就會使用到這三個命令。
3、實現原理:
(1)獲取鎖的時候,使用setnx加鎖,並使用expire命令為鎖添加一個超時時間,超過該時間則自動釋放鎖,鎖的value值為一個隨機生成的UUID,通過此在釋放鎖的時候進行判斷。
(2)獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
(3)釋放鎖的時候,通過UUID判斷是不是該鎖,若是該鎖,則執行delete進行鎖釋放。
三、基於zookeeper實現分布鎖
原理:ZooKeeper是一個為分布式應用提供一致性服務的開源組件,它內部是一個分層的文件系統目錄樹結構,規定同一個目錄下只能有一個唯一文件名
實現步驟:
(1)創建一個目錄mylock;
(2)線程A想獲取鎖就在mylock目錄下創建臨時順序節點;
(3)獲取mylock目錄下所有的子節點,然后獲取比自己小的兄弟節點,如果不存在,則說明當前線程順序號最小,獲得鎖;
(4)線程B獲取所有節點,判斷自己不是最小節點,設置監聽比自己次小的節點;
(5)線程A處理完,刪除自己的節點,線程B監聽到變更事件,判斷自己是不是最小的節點,如果是則獲得鎖。
這里推薦一個Apache的開源庫Curator,它是一個ZooKeeper客戶端,Curator提供的InterProcessMutex是分布式鎖的實現,acquire方法用於獲取鎖,release方法用於釋放鎖。
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
try {
return interProcessMutex.acquire(timeout, unit);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
public boolean unlock() {
try {
interProcessMutex.release();
} catch (Throwable e) {
log.error(e.getMessage(), e);
} finally {
executorService.schedule(new Cleaner(client, path), delayTimeForClean, TimeUnit.MILLISECONDS);
}
return true;
}
優點:具備高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。
缺點:因為需要頻繁的創建和刪除節點,性能上不如Redis方式。
參考:https://blog.csdn.net/xlgen157387/article/details/79036337
https://www.cnblogs.com/yuyutianxia/p/7149363.html
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。