柚子快報邀請碼778899分享:分布式鎖詳解
柚子快報邀請碼778899分享:分布式鎖詳解
網(wǎng)上有很多分布式鎖相關(guān)的文章,寫了一個相對簡潔易懂的版本,針對面試和工作應(yīng)該夠用了。
# 分布式鎖介紹
對于單機(jī)多線程來說,在 Java 中,我們通常使用 ReetrantLock 類、synchronized 關(guān)鍵字這類 JDK 自帶的 本地鎖 來控制一個 JVM 進(jìn)程內(nèi)的多個線程對本地共享資源的訪問。
下面是我對本地鎖畫的一張示意圖。
從圖中可以看出,這些線程訪問共享資源是互斥的,同一時刻只有一個線程可以獲取到本地鎖訪問共享資源。
分布式系統(tǒng)下,不同的服務(wù)/客戶端通常運(yùn)行在獨(dú)立的 JVM 進(jìn)程上。如果多個 JVM 進(jìn)程共享同一份資源的話,使用本地鎖就沒辦法實現(xiàn)資源的互斥訪問了。于是,分布式鎖 就誕生了。
舉個例子:系統(tǒng)的訂單服務(wù)一共部署了 3 份,都對外提供服務(wù)。用戶下訂單之前需要檢查庫存,為了防止超賣,這里需要加鎖以實現(xiàn)對檢查庫存操作的同步訪問。由于訂單服務(wù)位于不同的 JVM 進(jìn)程中,本地鎖在這種情況下就沒辦法正常工作了。我們需要用到分布式鎖,這樣的話,即使多個線程不在同一個 JVM 進(jìn)程中也能獲取到同一把鎖,進(jìn)而實現(xiàn)共享資源的互斥訪問。
下面是我對分布式鎖畫的一張示意圖。
從圖中可以看出,這些獨(dú)立的進(jìn)程中的線程訪問共享資源是互斥的,同一時刻只有一個線程可以獲取到分布式鎖訪問共享資源。
一個最基本的分布式鎖需要滿足:
互斥 :任意一個時刻,鎖只能被一個線程持有;高可用 :鎖服務(wù)是高可用的。并且,即使客戶端的釋放鎖的代碼邏輯出現(xiàn)問題,鎖最終一定還是會被釋放,不會影響其他線程對共享資源的訪問。可重入:一個節(jié)點(diǎn)獲取了鎖之后,還可以再次獲取鎖。
通常情況下,我們一般會選擇基于 Redis 或者 ZooKeeper 實現(xiàn)分布式鎖,Redis 用的要更多一點(diǎn),我這里也以 Redis 為例介紹分布式鎖的實現(xiàn)。
# 基于 Redis 實現(xiàn)分布式鎖
# 如何基于 Redis 實現(xiàn)一個最簡易的分布式鎖?
不論是本地鎖還是分布式鎖,核心都在于“互斥”。
在 Redis 中, SETNX 命令是可以幫助我們實現(xiàn)互斥。SETNX 即 SET if Not eXists (對應(yīng) Java 中的 setIfAbsent 方法),如果 key 不存在的話,才會設(shè)置 key 的值。如果 key 已經(jīng)存在, SETNX 啥也不做。
> SETNX lockKey uniqueValue
(integer) 1
> SETNX lockKey uniqueValue
(integer) 0
釋放鎖的話,直接通過 DEL 命令刪除對應(yīng)的 key 即可。
> DEL lockKey
(integer) 1
為了防止誤刪到其他的鎖,這里我們建議使用 Lua 腳本通過 key 對應(yīng)的 value(唯一值)來判斷。
選用 Lua 腳本是為了保證解鎖操作的原子性。因為 Redis 在執(zhí)行 Lua 腳本時,可以以原子性的方式執(zhí)行,從而保證了鎖釋放操作的原子性。
// 釋放鎖時,先比較鎖對應(yīng)的 value 值是否相等,避免鎖的誤釋放
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
這是一種最簡易的 Redis 分布式鎖實現(xiàn),實現(xiàn)方式比較簡單,性能也很高效。不過,這種方式實現(xiàn)分布式鎖存在一些問題。就比如應(yīng)用程序遇到一些問題比如釋放鎖的邏輯突然掛掉,可能會導(dǎo)致鎖無法被釋放,進(jìn)而造成共享資源無法再被其他線程/進(jìn)程訪問。
# 為什么要給鎖設(shè)置一個過期時間?
為了避免鎖無法被釋放,我們可以想到的一個解決辦法就是: 給這個 key(也就是鎖) 設(shè)置一個過期時間 。
127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
OK
lockKey :加鎖的鎖名;uniqueValue :能夠唯一標(biāo)示鎖的隨機(jī)字符串;NX :只有當(dāng) lockKey 對應(yīng)的 key 值不存在的時候才能 SET 成功;EX :過期時間設(shè)置(秒為單位)EX 3 標(biāo)示這個鎖有一個 3 秒的自動過期時間。與 EX 對應(yīng)的是 PX(毫秒為單位),這兩個都是過期時間設(shè)置。
一定要保證設(shè)置指定 key 的值和過期時間是一個原子操作?。?! 不然的話,依然可能會出現(xiàn)鎖無法被釋放的問題。
這樣確實可以解決問題,不過,這種解決辦法同樣存在漏洞:如果操作共享資源的時間大于過期時間,就會出現(xiàn)鎖提前過期的問題,進(jìn)而導(dǎo)致分布式鎖直接失效。如果鎖的超時時間設(shè)置過長,又會影響到性能。
你或許在想: 如果操作共享資源的操作還未完成,鎖過期時間能夠自己續(xù)期就好了!
# 如何實現(xiàn)鎖的優(yōu)雅續(xù)期?
對于 Java 開發(fā)的小伙伴來說,已經(jīng)有了現(xiàn)成的解決方案:Redissonopen in new window 。其他語言的解決方案,可以在 Redis 官方文檔中找到,地址:https://redis.io/topics/distlock 。
Redisson 是一個開源的 Java 語言 Redis 客戶端,提供了很多開箱即用的功能,不僅僅包括多種分布式鎖的實現(xiàn)。并且,Redisson 還支持 Redis 單機(jī)、Redis Sentinel 、Redis Cluster 等多種部署架構(gòu)。
Redisson 中的分布式鎖自帶自動續(xù)期機(jī)制,使用起來非常簡單,原理也比較簡單,其提供了一個專門用來監(jiān)控和續(xù)期鎖的 Watch Dog( 看門狗),如果操作共享資源的線程還未執(zhí)行完成的話,Watch Dog 會不斷地延長鎖的過期時間,進(jìn)而保證鎖不會因為超時而被釋放。
看門狗名字的由來于 getLockWatchdogTimeout() 方法,這個方法返回的是看門狗給鎖續(xù)期的過期時間,默認(rèn)為 30 秒(redisson-3.17.6open in new window)。
//默認(rèn) 30秒,支持修改
private long lockWatchdogTimeout = 30 * 1000;
public Config setLockWatchdogTimeout(long lockWatchdogTimeout) {
this.lockWatchdogTimeout = lockWatchdogTimeout;
return this;
}
public long getLockWatchdogTimeout() {
return lockWatchdogTimeout;
}
renewExpiration() 方法包含了看門狗的主要邏輯:
private void renewExpiration() {
//......
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
//......
// 異步續(xù)期,基于 Lua 腳本
CompletionStage
future.whenComplete((res, e) -> {
if (e != null) {
// 無法續(xù)期
log.error("Can't update lock " + getRawName() + " expiration", e);
EXPIRATION_RENEWAL_MAP.remove(getEntryName());
return;
}
if (res) {
// 遞歸調(diào)用實現(xiàn)續(xù)期
renewExpiration();
} else {
// 取消續(xù)期
cancelExpirationRenewal(null);
}
});
}
// 延遲 internalLockLeaseTime/3(默認(rèn) 10s,也就是 30/3) 再調(diào)用
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
默認(rèn)情況下,每過 10 秒,看門狗就會執(zhí)行續(xù)期操作,將鎖的超時時間設(shè)置為 30 秒??撮T狗續(xù)期前也會先判斷是否需要執(zhí)行續(xù)期操作,需要才會執(zhí)行續(xù)期,否則取消續(xù)期操作。
Watch Dog 通過調(diào)用 renewExpirationAsync() 方法實現(xiàn)鎖的異步續(xù)期:
protected CompletionStage
return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
// 判斷是否為持鎖線程,如果是就執(zhí)行續(xù)期操作,就鎖的過期時間設(shè)置為 30s(默認(rèn))
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return 1; " +
"end; " +
"return 0;",
Collections.singletonList(getRawName()),
internalLockLeaseTime, getLockName(threadId));
}
可以看出, renewExpirationAsync 方法其實是調(diào)用 Lua 腳本實現(xiàn)的續(xù)期,這樣做主要是為了保證續(xù)期操作的原子性。
我這里以 Redisson 的分布式可重入鎖 RLock 為例來說明如何使用 Redisson 實現(xiàn)分布式鎖:
// 1.獲取指定的分布式鎖對象
RLock lock = redisson.getLock("lock");
// 2.拿鎖且不設(shè)置鎖超時時間,具備 Watch Dog 自動續(xù)期機(jī)制
lock.lock();
// 3.執(zhí)行業(yè)務(wù)
...
// 4.釋放鎖
lock.unlock();
只有未指定鎖超時時間,才會使用到 Watch Dog 自動續(xù)期機(jī)制。
// 手動給鎖設(shè)置過期時間,不具備 Watch Dog 自動續(xù)期機(jī)制
lock.lock(10, TimeUnit.SECONDS);
如果使用 Redis 來實現(xiàn)分布式鎖的話,還是比較推薦直接基于 Redisson 來做的。
# 如何實現(xiàn)可重入鎖?
所謂可重入鎖指的是在一個線程中可以多次獲取同一把鎖,比如一個線程在執(zhí)行一個帶鎖的方法,該方法中又調(diào)用了另一個需要相同鎖的方法,則該線程可以直接執(zhí)行調(diào)用的方法即可重入 ,而無需重新獲得鎖。像 Java 中的 synchronized 和 ReentrantLock 都屬于可重入鎖。
不可重入的分布式鎖基本可以滿足絕大部分業(yè)務(wù)場景了,一些特殊的場景可能會需要使用可重入的分布式鎖。
可重入分布式鎖的實現(xiàn)核心思路是線程在獲取鎖的時候判斷是否為自己的鎖,如果是的話,就不用再重新獲取了。為此,我們可以為每個鎖關(guān)聯(lián)一個可重入計數(shù)器和一個占有它的線程。當(dāng)可重入計數(shù)器大于 0 時,則鎖被占有,需要判斷占有該鎖的線程和請求獲取鎖的線程是否為同一個。
實際項目中,我們不需要自己手動實現(xiàn),推薦使用我們上面提到的 Redisson ,其內(nèi)置了多種類型的鎖比如可重入鎖(Reentrant Lock)、自旋鎖(Spin Lock)、公平鎖(Fair Lock)、多重鎖(MultiLock)、 紅鎖(RedLock)、 讀寫鎖(ReadWriteLock)。
# Redis 如何解決集群情況下分布式鎖的可靠性?
為了避免單點(diǎn)故障,生產(chǎn)環(huán)境下的 Redis 服務(wù)通常是集群化部署的。
Redis 集群下,上面介紹到的分布式鎖的實現(xiàn)會存在一些問題。由于 Redis 集群數(shù)據(jù)同步到各個節(jié)點(diǎn)時是異步的,如果在 Redis 主節(jié)點(diǎn)獲取到鎖后,在沒有同步到其他節(jié)點(diǎn)時,Redis 主節(jié)點(diǎn)宕機(jī)了,此時新的 Redis 主節(jié)點(diǎn)依然可以獲取鎖,所以多個應(yīng)用服務(wù)就可以同時獲取到鎖。
針對這個問題,Redis 之父 antirez 設(shè)計了 Redlock 算法open in new window 來解決。
Redlock 算法的思想是讓客戶端向 Redis 集群中的多個獨(dú)立的 Redis 實例依次請求申請加鎖,如果客戶端能夠和半數(shù)以上的實例成功地完成加鎖操作,那么我們就認(rèn)為,客戶端成功地獲得分布式鎖,否則加鎖失敗。
即使部分 Redis 節(jié)點(diǎn)出現(xiàn)問題,只要保證 Redis 集群中有半數(shù)以上的 Redis 節(jié)點(diǎn)可用,分布式鎖服務(wù)就是正常的。
Redlock 是直接操作 Redis 節(jié)點(diǎn)的,并不是通過 Redis 集群操作的,這樣才可以避免 Redis 集群主從切換導(dǎo)致的鎖丟失問題。
Redlock 實現(xiàn)比較復(fù)雜,性能比較差,發(fā)生時鐘變遷的情況下還存在安全性隱患?!稊?shù)據(jù)密集型應(yīng)用系統(tǒng)設(shè)計》一書的作者 Martin Kleppmann 曾經(jīng)專門發(fā)文(How to do distributed locking - Martin Kleppmann - 2016open in new window)懟過 Redlock,他認(rèn)為這是一個很差的分布式鎖實現(xiàn)。感興趣的朋友可以看看Redis 鎖從面試連環(huán)炮聊到神仙打架open in new window這篇文章,有詳細(xì)介紹到 antirez 和 Martin Kleppmann 關(guān)于 Redlock 的激烈辯論。
實際項目中不建議使用 Redlock 算法,成本和收益不成正比。
如果不是非要實現(xiàn)絕對可靠的分布式鎖的話,其實單機(jī)版 Redis 就完全夠了,實現(xiàn)簡單,性能也非常高。如果你必須要實現(xiàn)一個絕對可靠的分布式鎖的話,可以基于 ZooKeeper 來做,只是性能會差一些。
# 基于 ZooKeeper 實現(xiàn)分布式鎖
Redis 實現(xiàn)分布式鎖性能較高,ZooKeeper 實現(xiàn)分布式鎖可靠性更高。實際項目中,我們應(yīng)該根據(jù)業(yè)務(wù)的具體需求來選擇。
# 如何基于 ZooKeeper 實現(xiàn)分布式鎖?
ZooKeeper 分布式鎖是基于 臨時順序節(jié)點(diǎn) 和 Watcher(事件監(jiān)聽器) 實現(xiàn)的。
獲取鎖:
首先我們要有一個持久節(jié)點(diǎn)/locks,客戶端獲取鎖就是在locks下創(chuàng)建臨時順序節(jié)點(diǎn)。假設(shè)客戶端 1 創(chuàng)建了/locks/lock1節(jié)點(diǎn),創(chuàng)建成功之后,會判斷 lock1是否是 /locks 下最小的子節(jié)點(diǎn)。如果 lock1是最小的子節(jié)點(diǎn),則獲取鎖成功。否則,獲取鎖失敗。如果獲取鎖失敗,則說明有其他的客戶端已經(jīng)成功獲取鎖??蛻舳?1 并不會不停地循環(huán)去嘗試加鎖,而是在前一個節(jié)點(diǎn)比如/locks/lock0上注冊一個事件監(jiān)聽器。這個監(jiān)聽器的作用是當(dāng)前一個節(jié)點(diǎn)釋放鎖之后通知客戶端 1(避免無效自旋),這樣客戶端 1 就加鎖成功了。
釋放鎖:
成功獲取鎖的客戶端在執(zhí)行完業(yè)務(wù)流程之后,會將對應(yīng)的子節(jié)點(diǎn)刪除。成功獲取鎖的客戶端在出現(xiàn)故障之后,對應(yīng)的子節(jié)點(diǎn)由于是臨時順序節(jié)點(diǎn),也會被自動刪除,避免了鎖無法被釋放。我們前面說的事件監(jiān)聽器其實監(jiān)聽的就是這個子節(jié)點(diǎn)刪除事件,子節(jié)點(diǎn)刪除就意味著鎖被釋放。
實際項目中,推薦使用 Curator 來實現(xiàn) ZooKeeper 分布式鎖。Curator 是 Netflix 公司開源的一套 ZooKeeper Java 客戶端框架,相比于 ZooKeeper 自帶的客戶端 zookeeper 來說,Curator 的封裝更加完善,各種 API 都可以比較方便地使用。
Curator主要實現(xiàn)了下面四種鎖:
InterProcessMutex:分布式可重入排它鎖InterProcessSemaphoreMutex:分布式不可重入排它鎖InterProcessReadWriteLock:分布式讀寫鎖InterProcessMultiLock:將多個鎖作為單個實體管理的容器,獲取鎖的時候獲取所有鎖,釋放鎖也會釋放所有鎖資源(忽略釋放失敗的鎖)。
CuratorFramework client = ZKUtils.getClient();
client.start();
// 分布式可重入排它鎖
InterProcessLock lock1 = new InterProcessMutex(client, lockPath1);
// 分布式不可重入排它鎖
InterProcessLock lock2 = new InterProcessSemaphoreMutex(client, lockPath2);
// 將多個鎖作為一個整體
InterProcessMultiLock lock = new InterProcessMultiLock(Arrays.asList(lock1, lock2));
if (!lock.acquire(10, TimeUnit.SECONDS)) {
throw new IllegalStateException("不能獲取多鎖");
}
System.out.println("已獲取多鎖");
System.out.println("是否有第一個鎖: " + lock1.isAcquiredInThisProcess());
System.out.println("是否有第二個鎖: " + lock2.isAcquiredInThisProcess());
try {
// 資源操作
resource.use();
} finally {
System.out.println("釋放多個鎖");
lock.release();
}
System.out.println("是否有第一個鎖: " + lock1.isAcquiredInThisProcess());
System.out.println("是否有第二個鎖: " + lock2.isAcquiredInThisProcess());
client.close();
# 為什么要用臨時順序節(jié)點(diǎn)?
每個數(shù)據(jù)節(jié)點(diǎn)在 ZooKeeper 中被稱為 znode,它是 ZooKeeper 中數(shù)據(jù)的最小單元。
我們通常是將 znode 分為 4 大類:
持久(PERSISTENT)節(jié)點(diǎn) :一旦創(chuàng)建就一直存在即使 ZooKeeper 集群宕機(jī),直到將其刪除。臨時(EPHEMERAL)節(jié)點(diǎn) :臨時節(jié)點(diǎn)的生命周期是與 客戶端會話(session) 綁定的,會話消失則節(jié)點(diǎn)消失 。并且,臨時節(jié)點(diǎn)只能做葉子節(jié)點(diǎn) ,不能創(chuàng)建子節(jié)點(diǎn)。持久順序(PERSISTENT_SEQUENTIAL)節(jié)點(diǎn) :除了具有持久(PERSISTENT)節(jié)點(diǎn)的特性之外, 子節(jié)點(diǎn)的名稱還具有順序性。比如 /node1/app0000000001 、/node1/app0000000002 。臨時順序(EPHEMERAL_SEQUENTIAL)節(jié)點(diǎn) :除了具備臨時(EPHEMERAL)節(jié)點(diǎn)的特性之外,子節(jié)點(diǎn)的名稱還具有順序性。
可以看出,臨時節(jié)點(diǎn)相比持久節(jié)點(diǎn),最主要的是對會話失效的情況處理不一樣,臨時節(jié)點(diǎn)會話消失則對應(yīng)的節(jié)點(diǎn)消失。這樣的話,如果客戶端發(fā)生異常導(dǎo)致沒來得及釋放鎖也沒關(guān)系,會話失效節(jié)點(diǎn)自動被刪除,不會發(fā)生死鎖的問題。
使用 Redis 實現(xiàn)分布式鎖的時候,我們是通過過期時間來避免鎖無法被釋放導(dǎo)致死鎖問題的,而 ZooKeeper 直接利用臨時節(jié)點(diǎn)的特性即可。
假設(shè)不適用順序節(jié)點(diǎn)的話,所有嘗試獲取鎖的客戶端都會對持有鎖的子節(jié)點(diǎn)加監(jiān)聽器。當(dāng)該鎖被釋放之后,勢必會造成所有嘗試獲取鎖的客戶端來爭奪鎖,這樣對性能不友好。使用順序節(jié)點(diǎn)之后,只需要監(jiān)聽前一個節(jié)點(diǎn)就好了,對性能更友好。
# 為什么要設(shè)置對前一個節(jié)點(diǎn)的監(jiān)聽?
Watcher(事件監(jiān)聽器),是 ZooKeeper 中的一個很重要的特性。ZooKeeper 允許用戶在指定節(jié)點(diǎn)上注冊一些 Watcher,并且在一些特定事件觸發(fā)的時候,ZooKeeper 服務(wù)端會將事件通知到感興趣的客戶端上去,該機(jī)制是 ZooKeeper 實現(xiàn)分布式協(xié)調(diào)服務(wù)的重要特性。
同一時間段內(nèi),可能會有很多客戶端同時獲取鎖,但只有一個可以獲取成功。如果獲取鎖失敗,則說明有其他的客戶端已經(jīng)成功獲取鎖。獲取鎖失敗的客戶端并不會不停地循環(huán)去嘗試加鎖,而是在前一個節(jié)點(diǎn)注冊一個事件監(jiān)聽器。
這個事件監(jiān)聽器的作用是: 當(dāng)前一個節(jié)點(diǎn)對應(yīng)的客戶端釋放鎖之后(也就是前一個節(jié)點(diǎn)被刪除之后,監(jiān)聽的是刪除事件),通知獲取鎖失敗的客戶端(喚醒等待的線程,Java 中的 wait/notifyAll ),讓它嘗試去獲取鎖,然后就成功獲取鎖了。
# 如何實現(xiàn)可重入鎖?
這里以 Curator 的 InterProcessMutex 對可重入鎖的實現(xiàn)來介紹(源碼地址:InterProcessMutex.javaopen in new window)。
當(dāng)我們調(diào)用 InterProcessMutex#acquire方法獲取鎖的時候,會調(diào)用InterProcessMutex#internalLock方法。
// 獲取可重入互斥鎖,直到獲取成功為止
@Override
public void acquire() throws Exception {
if (!internalLock(-1, null)) {
throw new IOException("Lost connection while trying to acquire lock: " + basePath);
}
}
internalLock 方法會先獲取當(dāng)前請求鎖的線程,然后從 threadData( ConcurrentMap
第一次獲取鎖的時候,lockData為 null。獲取鎖成功之后,會將當(dāng)前線程和對應(yīng)的 lockData 放到 threadData 中
private boolean internalLock(long time, TimeUnit unit) throws Exception {
// 獲取當(dāng)前請求鎖的線程
Thread currentThread = Thread.currentThread();
// 拿對應(yīng)的 lockData
LockData lockData = threadData.get(currentThread);
// 第一次獲取鎖的話,lockData 為 null
if (lockData != null) {
// 當(dāng)前線程獲取過一次鎖之后
// 因為當(dāng)前線程的鎖存在, lockCount 自增后返回,實現(xiàn)鎖重入.
lockData.lockCount.incrementAndGet();
return true;
}
// 嘗試獲取鎖
String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());
if (lockPath != null) {
LockData newLockData = new LockData(currentThread, lockPath);
// 獲取鎖成功之后,將當(dāng)前線程和對應(yīng)的 lockData 放到 threadData 中
threadData.put(currentThread, newLockData);
return true;
}
return false;
}
LockData是 InterProcessMutex中的一個靜態(tài)內(nèi)部類。
private final ConcurrentMap
private static class LockData
{
// 當(dāng)前持有鎖的線程
final Thread owningThread;
// 鎖對應(yīng)的子節(jié)點(diǎn)
final String lockPath;
// 加鎖的次數(shù)
final AtomicInteger lockCount = new AtomicInteger(1);
private LockData(Thread owningThread, String lockPath)
{
this.owningThread = owningThread;
this.lockPath = lockPath;
}
}
如果已經(jīng)獲取過一次鎖,后面再來獲取鎖的話,直接就會在 if (lockData != null) 這里被攔下了,然后就會執(zhí)行l(wèi)ockData.lockCount.incrementAndGet(); 將加鎖次數(shù)加 1。
整個可重入鎖的實現(xiàn)邏輯非常簡單,直接在客戶端判斷當(dāng)前線程有沒有獲取鎖,有的話直接將加鎖次數(shù)加 1 就可以了。
# 總結(jié)
這篇文章我們介紹了分布式鎖的基本概念以及實現(xiàn)分布式鎖的兩種常見方式。至于具體選擇 Redis 還是 ZooKeeper 來實現(xiàn)分布式鎖,還是要看業(yè)務(wù)的具體需求。如果對性能要求比較高的話,建議使用 Redis 實現(xiàn)分布式鎖。如果對可靠性要求比較高的話,建議使用 ZooKeeper 實現(xiàn)分布式鎖。
柚子快報邀請碼778899分享:分布式鎖詳解
精彩內(nèi)容
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。