自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

搞懂“分布式鎖”,看這篇文章就對(duì)了

開發(fā) 后端 開發(fā)工具 分布式
對(duì)于鎖大家肯定不會(huì)陌生,在 Java 中 synchronized 關(guān)鍵字和 ReentrantLock 可重入鎖在我們的代碼中是經(jīng)常見的,一般我們用其在多線程環(huán)境中控制對(duì)資源的并發(fā)訪問。

但是隨著分布式的快速發(fā)展,本地的加鎖往往不能滿足我們的需要,在我們的分布式環(huán)境中上面加鎖的方法就會(huì)失去作用。

于是人們?yōu)榱嗽诜植际江h(huán)境中也能實(shí)現(xiàn)本地鎖的效果,也是紛紛各出其招,今天讓我們來聊一聊一般分布式鎖實(shí)現(xiàn)的套路。

為何需要分布式鎖

Martin Kleppmann 是英國(guó)劍橋大學(xué)的分布式系統(tǒng)的研究員,之前和 Redis 之父 Antirez 進(jìn)行過關(guān)于 RedLock(紅鎖,后續(xù)有講到)是否安全的激烈討論。

Martin 認(rèn)為一般我們使用分布式鎖有兩個(gè)場(chǎng)景:

  • 效率:使用分布式鎖可以避免不同節(jié)點(diǎn)重復(fù)相同的工作,這些工作會(huì)浪費(fèi)資源。比如用戶付了錢之后有可能不同節(jié)點(diǎn)會(huì)發(fā)出多封短信。
  • 正確性:加分布式鎖同樣可以避免破壞正確性的發(fā)生,如果兩個(gè)節(jié)點(diǎn)在同一條數(shù)據(jù)上面操作,比如多個(gè)節(jié)點(diǎn)機(jī)器對(duì)同一個(gè)訂單操作不同的流程有可能會(huì)導(dǎo)致該筆訂單最后狀態(tài)出現(xiàn)錯(cuò)誤,造成損失。

分布式鎖的一些特點(diǎn)

當(dāng)我們確定了在不同節(jié)點(diǎn)上需要分布式鎖,那么我們需要了解分布式鎖到底應(yīng)該有哪些特點(diǎn)?

分布式鎖的特點(diǎn)如下:

  • 互斥性:和我們本地鎖一樣互斥性是最基本,但是分布式鎖需要保證在不同節(jié)點(diǎn)的不同線程的互斥。
  • 可重入性:同一個(gè)節(jié)點(diǎn)上的同一個(gè)線程如果獲取了鎖之后那么也可以再次獲取這個(gè)鎖。
  • 鎖超時(shí):和本地鎖一樣支持鎖超時(shí),防止死鎖。
  • 高效,高可用:加鎖和解鎖需要高效,同時(shí)也需要保證高可用防止分布式鎖失效,可以增加降級(jí)。
  • 支持阻塞和非阻塞:和 ReentrantLock 一樣支持 lock 和 trylock 以及 tryLock(long timeOut)。
  • 支持公平鎖和非公平鎖(可選):公平鎖的意思是按照請(qǐng)求加鎖的順序獲得鎖,非公平鎖就相反是無序的。這個(gè)一般來說實(shí)現(xiàn)的比較少。

常見的分布式鎖

我們了解了一些特點(diǎn)之后,我們一般實(shí)現(xiàn)分布式鎖有以下幾個(gè)方式:

  • MySQL
  • ZK
  • Redis
  • 自研分布式鎖:如谷歌的 Chubby。

下面分開介紹一下這些分布式鎖的實(shí)現(xiàn)原理。

MySQL

首先來說一下 MySQL 分布式鎖的實(shí)現(xiàn)原理,相對(duì)來說這個(gè)比較容易理解,畢竟數(shù)據(jù)庫和我們開發(fā)人員在平時(shí)的開發(fā)中息息相關(guān)。

對(duì)于分布式鎖我們可以創(chuàng)建一個(gè)鎖表:

 

前面我們所說的 lock(),trylock(long timeout),trylock() 這幾個(gè)方法可以用下面的偽代碼實(shí)現(xiàn)。

lock()

lock 一般是阻塞式的獲取鎖,意思就是不獲取到鎖誓不罷休,那么我們可以寫一個(gè)死循環(huán)來執(zhí)行其操作:

 

mysqlLock.lcok 內(nèi)部是一個(gè) sql,為了達(dá)到可重入鎖的效果,我們應(yīng)該先進(jìn)行查詢,如果有值,需要比較 node_info 是否一致。

這里的 node_info 可以用機(jī)器 IP 和線程名字來表示,如果一致就加可重入鎖 count 的值,如果不一致就返回 false。如果沒有值就直接插入一條數(shù)據(jù)。

偽代碼如下:

 

需要注意的是這一段代碼需要加事務(wù),必須要保證這一系列操作的原子性。

tryLock() 和 tryLock(long timeout)

tryLock() 是非阻塞獲取鎖,如果獲取不到就會(huì)馬上返回,代碼如下:

 

tryLock(long timeout) 實(shí)現(xiàn)如下:

 

mysqlLock.lock 和上面一樣,但是要注意的是 select … for update 這個(gè)是阻塞的獲取行鎖,如果同一個(gè)資源并發(fā)量較大還是有可能會(huì)退化成阻塞的獲取鎖。

unlock()

unlock 的話如果這里的 count 為 1 那么可以刪除,如果大于 1 那么需要減去 1。

 

鎖超時(shí)

我們有可能會(huì)遇到我們的機(jī)器節(jié)點(diǎn)掛了,那么這個(gè)鎖就不會(huì)得到釋放,我們可以啟動(dòng)一個(gè)定時(shí)任務(wù),通過計(jì)算一般我們處理任務(wù)的時(shí)間。

比如是 5ms,那么我們可以稍微擴(kuò)大一點(diǎn),當(dāng)這個(gè)鎖超過 20ms 沒有被釋放我們就可以認(rèn)定是節(jié)點(diǎn)掛了然后將其直接釋放。

MySQL 小結(jié):

  • 適用場(chǎng)景:MySQL 分布式鎖一般適用于資源不存在數(shù)據(jù)庫,如果數(shù)據(jù)庫存在比如訂單,可以直接對(duì)這條數(shù)據(jù)加行鎖,不需要我們上面多的繁瑣的步驟。

比如一個(gè)訂單,我們可以用 select * from order_table where id = 'xxx' for update 進(jìn)行加行鎖,那么其他的事務(wù)就不能對(duì)其進(jìn)行修改。

  • 優(yōu)點(diǎn):理解起來簡(jiǎn)單,不需要維護(hù)額外的第三方中間件(比如 Redis,ZK)。
  • 缺點(diǎn):雖然容易理解但是實(shí)現(xiàn)起來較為繁瑣,需要自己考慮鎖超時(shí),加事務(wù)等等。性能局限于數(shù)據(jù)庫,一般對(duì)比緩存來說性能較低。對(duì)于高并發(fā)的場(chǎng)景并不是很適合。

樂觀鎖

前面我們介紹的都是悲觀鎖,這里想額外提一下樂觀鎖,在我們實(shí)際項(xiàng)目中也是經(jīng)常實(shí)現(xiàn)樂觀鎖,因?yàn)槲覀兗有墟i的性能消耗比較大,通常我們對(duì)于一些競(jìng)爭(zhēng)不是那么激烈。

但是其又需要保證我們并發(fā)的順序執(zhí)行使用樂觀鎖進(jìn)行處理,我們可以對(duì)我們的表加一個(gè)版本號(hào)字段。

那么我們查詢出來一個(gè)版本號(hào)之后,update 或者 delete 的時(shí)候需要依賴我們查詢出來的版本號(hào),判斷當(dāng)前數(shù)據(jù)庫和查詢出來的版本號(hào)是否相等,如果相等那么就可以執(zhí)行,如果不等那么就不能執(zhí)行。

這樣的一個(gè)策略很像我們的 CAS(Compare And Swap),比較并交換是一個(gè)原子操作。這樣我們就能避免加 select * for update 行鎖的開銷。

ZooKeeper

ZooKeeper 也是我們常見的實(shí)現(xiàn)分布式鎖方法,相比于數(shù)據(jù)庫如果沒了解過 ZooKeeper 可能上手比較難一些。

ZooKeeper 是以 Paxos 算法為基礎(chǔ)的分布式應(yīng)用程序協(xié)調(diào)服務(wù)。ZK 的數(shù)據(jù)節(jié)點(diǎn)和文件目錄類似,所以我們可以用此特性實(shí)現(xiàn)分布式鎖。

我們以某個(gè)資源為目錄,然后這個(gè)目錄下面的節(jié)點(diǎn)就是我們需要獲取鎖的客戶端,未獲取到鎖的客戶端注冊(cè)需要注冊(cè) Watcher 到上一個(gè)客戶端,可以用下圖表示:

 

/lock 是我們用于加鎖的目錄,/resource_name 是我們鎖定的資源,其下面的節(jié)點(diǎn)按照我們加鎖的順序排列。

Curator

Curator 封裝了 ZooKeeper 底層的 API,使我們更加容易方便的對(duì) ZooKeeper 進(jìn)行操作,并且它封裝了分布式鎖的功能,這樣我們就不需要在自己實(shí)現(xiàn)了。

Curator 實(shí)現(xiàn)了可重入鎖(InterProcessMutex),也實(shí)現(xiàn)了不可重入鎖(InterProcessSemaphoreMutex)。在可重入鎖中還實(shí)現(xiàn)了讀寫鎖。

InterProcessMutex

InterProcessMutex 是 Curator 實(shí)現(xiàn)的可重入鎖,我們可以通過下面的一段代碼實(shí)現(xiàn)我們的可重入鎖:

 

我們利用 acuire 進(jìn)行加鎖,release 進(jìn)行解鎖。

加鎖的流程具體如下:

  • 首先進(jìn)行可重入的判定:這里的可重入鎖記錄在 ConcurrentMap

如果 threadData.get(currentThread)是有值的那么就證明是可重入鎖,然后記錄就會(huì)加 1。

我們之前的 MySQL 其實(shí)也可以通過這種方法去優(yōu)化,可以不需要 count 字段的值,將這個(gè)維護(hù)在本地可以提高性能。

  • 然后在我們的資源目錄下創(chuàng)建一個(gè)節(jié)點(diǎn):比如這里創(chuàng)建一個(gè) /0000000002 這個(gè)節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)需要設(shè)置為 EPHEMERAL_SEQUENTIAL 也就是臨時(shí)節(jié)點(diǎn)并且有序。
  • 獲取當(dāng)前目錄下所有子節(jié)點(diǎn),判斷自己的節(jié)點(diǎn)是否位于子節(jié)點(diǎn)第一個(gè)。
  • 如果是第一個(gè),則獲取到鎖,那么可以返回。
  • 如果不是第一個(gè),則證明前面已經(jīng)有人獲取到鎖了,那么需要獲取自己節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)。

/0000000002 的前一個(gè)節(jié)點(diǎn)是 /0000000001,我們獲取到這個(gè)節(jié)點(diǎn)之后,再上面注冊(cè) Watcher(這里的 Watcher 其實(shí)調(diào)用的是 object.notifyAll(),用來解除阻塞)。

  • object.wait(timeout) 或 object.wait():進(jìn)行阻塞等待,這里和我們第 5 步的 Watcher 相對(duì)應(yīng)。

解鎖的具體流程:

  • 首先進(jìn)行可重入鎖的判定:如果有可重入鎖只需要次數(shù)減 1 即可,減 1 之后加鎖次數(shù)為 0 的話繼續(xù)下面步驟,不為 0 直接返回。
  • 刪除當(dāng)前節(jié)點(diǎn)。
  • 刪除 threadDataMap 里面的可重入鎖的數(shù)據(jù)。

讀寫鎖

Curator 提供了讀寫鎖,其實(shí)現(xiàn)類是 InterProcessReadWriteLock,這里的每個(gè)節(jié)點(diǎn)都會(huì)加上前綴:

  1. private static final String READ_LOCK_NAME  = "__READ__"
  2. private static final String WRITE_LOCK_NAME = "__WRIT__"

根據(jù)不同的前綴區(qū)分是讀鎖還是寫鎖,對(duì)于讀鎖,如果發(fā)現(xiàn)前面有寫鎖,那么需要將 Watcher 注冊(cè)到和自己最近的寫鎖。寫鎖的邏輯和我們之前 4.2 分析的依然保持不變。

鎖超時(shí)

ZooKeeper 不需要配置鎖超時(shí),由于我們?cè)O(shè)置節(jié)點(diǎn)是臨時(shí)節(jié)點(diǎn),我們的每個(gè)機(jī)器維護(hù)著一個(gè) ZK 的 Session,通過這個(gè) Session,ZK 可以判斷機(jī)器是否宕機(jī)。

如果我們的機(jī)器掛掉的話,那么這個(gè)臨時(shí)節(jié)點(diǎn)對(duì)應(yīng)的就會(huì)被刪除,所以我們不需要關(guān)心鎖超時(shí)。

ZK 小結(jié):

  • 優(yōu)點(diǎn):ZK 可以不需要關(guān)心鎖超時(shí)時(shí)間,實(shí)現(xiàn)起來有現(xiàn)成的第三方包,比較方便,并且支持讀寫鎖,ZK 獲取鎖會(huì)按照加鎖的順序,所以其是公平鎖。對(duì)于高可用利用 ZK 集群進(jìn)行保證。
  • 缺點(diǎn):ZK 需要額外維護(hù),增加維護(hù)成本,性能和 MySQL 相差不大,依然比較差。并且需要開發(fā)人員了解 ZK 是什么。

Redis

大家在網(wǎng)上搜索分布式鎖,恐怕最多的實(shí)現(xiàn)就是 Redis 了,Redis 因?yàn)槠湫阅芎?,?shí)現(xiàn)起來簡(jiǎn)單所以讓很多人都對(duì)其十分青睞。

Redis 分布式鎖簡(jiǎn)單實(shí)現(xiàn)

熟悉 Redis 的同學(xué)那么肯定對(duì) setNx(set if not exist) 方法不陌生,如果不存在則更新,其可以很好的用來實(shí)現(xiàn)我們的分布式鎖。

對(duì)于某個(gè)資源加鎖我們只需要:

  1. setNx resourceName value 

這里有個(gè)問題,加鎖了之后如果機(jī)器宕機(jī)那么這個(gè)鎖就不會(huì)得到釋放所以會(huì)加入過期時(shí)間,加入過期時(shí)間需要和 setNx 同一個(gè)原子操作。

在 Redis 2.8 之前我們需要使用 Lua 腳本達(dá)到我們的目的,但是 Redis 2.8 之后 Redis 支持 nx 和 ex 操作是同一原子操作。

  1. set resourceName value ex 5 nx 

Redission

Javaer 都知道 Jedis,Jedis 是 Redis 的 Java 實(shí)現(xiàn)的客戶端,其 API 提供了比較全面的 Redis 命令的支持。

Redission 也是 Redis 的客戶端,相比于 Jedis 功能簡(jiǎn)單。Jedis 簡(jiǎn)單使用阻塞的 I/O 和 Redis 交互,Redission 通過 Netty 支持非阻塞 I/O。

Jedis 最新版本 2.9.0 是 2016 年的快 3 年了沒有更新,而 Redission 最新版本是 2018 年 10 月更新。

Redission 封裝了鎖的實(shí)現(xiàn),其繼承了 java.util.concurrent.locks.Lock 的接口,讓我們像操作我們的本地 Lock 一樣去操作 Redission 的 Lock。

下面介紹一下其如何實(shí)現(xiàn)分布式鎖:

 

 

 

Redission 不僅提供了 Java 自帶的一些方法(lock,tryLock),還提供了異步加鎖,對(duì)于異步編程更加方便。

由于內(nèi)部源碼較多,就不貼源碼了,這里用文字?jǐn)⑹鰜矸治鏊侨绾渭渔i的,這里分析一下 tryLock 方法:

①嘗試加鎖:首先會(huì)嘗試進(jìn)行加鎖,由于需要兼容老版本的 Redis,所以不能直接使用 ex,nx 原子操作的 API,那么就只能使用 Lua 腳本,相關(guān)的 Lua 腳本如下:

 

可以看見它并沒有使用我們的 sexNx 來進(jìn)行操作,而是使用的 hash 結(jié)構(gòu),我們的每一個(gè)需要鎖定的資源都可以看做是一個(gè) HashMap,鎖定資源的節(jié)點(diǎn)信息是 Key,鎖定次數(shù)是 Value。

通過這種方式可以很好的實(shí)現(xiàn)可重入的效果,只需要對(duì) Value 進(jìn)行加 1 操作,就能進(jìn)行可重入鎖。當(dāng)然這里也可以用之前我們說的本地計(jì)數(shù)進(jìn)行優(yōu)化。

②如果嘗試加鎖失敗,判斷是否超時(shí),如果超時(shí)則返回 false。

③如果加鎖失敗之后,沒有超時(shí),那么需要在名字為 redisson_lock__channel+lockName 的 channel 上進(jìn)行訂閱,用于訂閱解鎖消息,然后一直阻塞直到超時(shí),或者有解鎖消息。

④重試步驟 1,2,3,直到最后獲取到鎖,或者某一步獲取鎖超時(shí)。

對(duì)于我們的 unlock 方法比較簡(jiǎn)單也是通過 lua 腳本進(jìn)行解鎖,如果是可重入鎖,只是減 1。如果是非加鎖線程解鎖,那么解鎖失敗。

 

Redission 還有公平鎖的實(shí)現(xiàn),對(duì)于公平鎖其利用了 list 結(jié)構(gòu)和 hashset 結(jié)構(gòu)分別用來保存我們排隊(duì)的節(jié)點(diǎn),和我們節(jié)點(diǎn)的過期時(shí)間,用這兩個(gè)數(shù)據(jù)結(jié)構(gòu)幫助我們實(shí)現(xiàn)公平鎖,這里就不展開介紹了,有興趣可以參考源碼。

RedLock

我們想象一個(gè)這樣的場(chǎng)景當(dāng)機(jī)器 A 申請(qǐng)到一把鎖之后,如果 Redis 主宕機(jī)了,這個(gè)時(shí)候從機(jī)并沒有同步到這一把鎖,那么機(jī)器 B 再次申請(qǐng)的時(shí)候就會(huì)再次申請(qǐng)到這把鎖。

為了解決這個(gè)問題 Redis 作者提出了 RedLock 紅鎖的算法,在 Redission 中也對(duì) RedLock 進(jìn)行了實(shí)現(xiàn)。

 

通過上面的代碼,我們需要實(shí)現(xiàn)多個(gè) Redis 集群,然后進(jìn)行紅鎖的加鎖,解鎖。

具體的步驟如下:

①首先生成多個(gè) Redis 集群的 Rlock,并將其構(gòu)造成 RedLock。

②依次循環(huán)對(duì)三個(gè)集群進(jìn)行加鎖,加鎖的過程和 5.2 里面一致。

③如果循環(huán)加鎖的過程中加鎖失敗,那么需要判斷加鎖失敗的次數(shù)是否超出了最大值,這里的最大值是根據(jù)集群的個(gè)數(shù),比如三個(gè)那么只允許失敗一個(gè),五個(gè)的話只允許失敗兩個(gè),要保證多數(shù)成功。

④加鎖的過程中需要判斷是否加鎖超時(shí),有可能我們?cè)O(shè)置加鎖只能用 3ms,第一個(gè)集群加鎖已經(jīng)消耗了 3ms 了。那么也算加鎖失敗。

⑤3,4 步里面加鎖失敗的話,那么就會(huì)進(jìn)行解鎖操作,解鎖會(huì)對(duì)所有的集群在請(qǐng)求一次解鎖。

可以看見 RedLock 基本原理是利用多個(gè) Redis 集群,用多數(shù)的集群加鎖成功,減少 Redis 某個(gè)集群出故障,造成分布式鎖出現(xiàn)問題的概率。

Redis 小結(jié):

  • 優(yōu)點(diǎn):對(duì)于 Redis 實(shí)現(xiàn)簡(jiǎn)單,性能對(duì)比 ZK 和 MySQL 較好。如果不需要特別復(fù)雜的要求,自己就可以利用 setNx 進(jìn)行實(shí)現(xiàn),如果自己需要復(fù)雜的需求的話,可以利用或者借鑒 Redission。對(duì)于一些要求比較嚴(yán)格的場(chǎng)景可以使用 RedLock。
  • 缺點(diǎn):需要維護(hù) Redis 集群,如果要實(shí)現(xiàn) RedLock 需要維護(hù)更多的集群。

分布式鎖的安全問題

上面我們介紹過紅鎖,但是 Martin Kleppmann 認(rèn)為其依然不安全。

有關(guān)于 Martin 反駁的幾點(diǎn),我認(rèn)為其實(shí)不僅僅局限于 RedLock,前面說的算法基本都有這個(gè)問題,下面我們來討論一下這些問題。

長(zhǎng)時(shí)間的 GC pause

熟悉 Java 的同學(xué)肯定對(duì) GC 不陌生,在 GC 的時(shí)候會(huì)發(fā)生 STW(stop-the-world)。

例如 CMS 垃圾回收器,它會(huì)有兩個(gè)階段進(jìn)行 STW 防止引用繼續(xù)進(jìn)行變化。那么有可能會(huì)出現(xiàn)下面圖(引用至 Martin 反駁 Redlock 的文章)中這個(gè)情況:

 

client1 獲取了鎖并且設(shè)置了鎖的超時(shí)時(shí)間,但是 client1 之后出現(xiàn)了 STW,這個(gè) STW 時(shí)間比較長(zhǎng),導(dǎo)致分布式鎖進(jìn)行了釋放。

client2 獲取到了鎖,這個(gè)時(shí)候 client1 恢復(fù)了鎖,那么就會(huì)出現(xiàn) client1,2 同時(shí)獲取到鎖,這個(gè)時(shí)候分布式鎖不安全問題就出現(xiàn)了。

這個(gè)不僅僅局限于 RedLock,對(duì)于我們的 ZK,MySQL 一樣的有同樣的問題。

時(shí)鐘發(fā)生跳躍

對(duì)于 Redis 服務(wù)器如果其時(shí)間發(fā)生了跳躍,肯定會(huì)影響我們鎖的過期時(shí)間。

那么我們的鎖過期時(shí)間就不是我們預(yù)期的了,也會(huì)出現(xiàn) client1 和 client2 獲取到同一把鎖,也會(huì)出現(xiàn)不安全,這個(gè)對(duì)于 MySQL 也會(huì)出現(xiàn)。但是 ZK 由于沒有設(shè)置過期時(shí)間,那么發(fā)生跳躍也不會(huì)受影響。

長(zhǎng)時(shí)間的網(wǎng)絡(luò) I/O

這個(gè)問題和我們的 GC 的 STW 很像,也就是我們這個(gè)獲取了鎖之后我們進(jìn)行網(wǎng)絡(luò)調(diào)用,其調(diào)用時(shí)間由可能比我們鎖的過期時(shí)間都還長(zhǎng),那么也會(huì)出現(xiàn)不安全的問題,這個(gè) MySQL 也會(huì)有,ZK 也不會(huì)出現(xiàn)這個(gè)問題。

對(duì)于這三個(gè)問題,在網(wǎng)上包括 Redis 作者在內(nèi)發(fā)起了很多討論。

GC 的 STW

對(duì)于這個(gè)問題可以看見基本所有的都會(huì)出現(xiàn)問題,Martin 給出了一個(gè)解法,對(duì)于 ZK 這種他會(huì)生成一個(gè)自增的序列,那么我們真正進(jìn)行對(duì)資源操作的時(shí)候,需要判斷當(dāng)前序列是否是最新,有點(diǎn)類似于樂觀鎖。

當(dāng)然這個(gè)解法 Redis 作者進(jìn)行了反駁,你既然都能生成一個(gè)自增的序列了那么你完全不需要加鎖了,也就是可以按照類似于 MySQL 樂觀鎖的解法去做。

我自己認(rèn)為這種解法增加了復(fù)雜性,當(dāng)我們對(duì)資源操作的時(shí)候需要增加判斷序列號(hào)是否是最新,無論用什么判斷方法都會(huì)增加復(fù)雜度,后面會(huì)介紹谷歌的 Chubby 提出了一個(gè)更好的方案。

時(shí)鐘發(fā)生跳躍

Martin 覺得 RedLock 不安全很大的原因也是因?yàn)闀r(shí)鐘的跳躍,因?yàn)殒i過期強(qiáng)依賴于時(shí)間,但是 ZK 不需要依賴時(shí)間,依賴每個(gè)節(jié)點(diǎn)的 Session。

Redis 作者也給出了解答,對(duì)于時(shí)間跳躍分為人為調(diào)整和 NTP 自動(dòng)調(diào)整:

  • 人為調(diào)整:人為調(diào)整影響的完全可以人為不調(diào)整,這個(gè)是處于可控的。
  • NTP 自動(dòng)調(diào)整:這個(gè)可以通過一定的優(yōu)化,把跳躍時(shí)間控制在可控范圍內(nèi),雖然會(huì)跳躍,但是是完全可以接受的。

長(zhǎng)時(shí)間的網(wǎng)絡(luò) I/O

這一塊不是他們討論的重點(diǎn),我自己覺得,對(duì)于這個(gè)問題的優(yōu)化可以控制網(wǎng)絡(luò)調(diào)用的超時(shí)時(shí)間,把所有網(wǎng)絡(luò)調(diào)用的超時(shí)時(shí)間相加。

那么我們鎖過期時(shí)間其實(shí)應(yīng)該大于這個(gè)時(shí)間,當(dāng)然也可以通過優(yōu)化網(wǎng)絡(luò)調(diào)用比如串行改成并行,異步化等。

Chubby 的一些優(yōu)化

大家搜索 ZK 的時(shí)候,會(huì)發(fā)現(xiàn)他們都寫了 ZK 是 Chubby 的開源實(shí)現(xiàn),Chubby 內(nèi)部工作原理和 ZK 類似。但是 Chubby 的定位是分布式鎖和 ZK 有點(diǎn)不同。

Chubby 也是使用上面自增序列的方案用來解決分布式不安全的問題,但是它提供了多種校驗(yàn)方法:

  • CheckSequencer():調(diào)用 Chubby 的 API 檢查此時(shí)這個(gè)序列號(hào)是否有效。
  • 訪問資源服務(wù)器檢查,判斷當(dāng)前資源服務(wù)器最新的序列號(hào)和我們的序列號(hào)的大小。
  • lock-delay:為了防止我們校驗(yàn)的邏輯入侵我們的資源服務(wù)器,其提供了一種方法當(dāng)客戶端失聯(lián)的時(shí)候,并不會(huì)立即釋放鎖,而是在一定的時(shí)間內(nèi)(默認(rèn) 1min)阻止其他客戶端拿去這個(gè)鎖。

那么也就是給予了一定的 buffer 等待 STW 恢復(fù),而我們的 GC 的 STW 時(shí)間如果比 1min 還長(zhǎng)那么你應(yīng)該檢查你的程序,而不是懷疑你的分布式鎖了。

小結(jié)

本文主要講了多種分布式鎖的實(shí)現(xiàn)方法,以及它們的一些優(yōu)缺點(diǎn)。最后也說了一下關(guān)于分布式鎖的安全的問題。

對(duì)于不同的業(yè)務(wù)需要的安全程度完全不同,我們需要根據(jù)自己的業(yè)務(wù)場(chǎng)景,通過不同的維度分析,選取最適合自己的方案。

 

責(zé)任編輯:武曉燕 來源: 咖啡拿鐵
相關(guān)推薦

2021-10-21 06:52:17

ZooKeeper分布式配置

2017-03-30 22:41:55

虛擬化操作系統(tǒng)軟件

2021-11-10 07:47:48

Traefik邊緣網(wǎng)關(guān)

2018-12-07 09:31:52

分布式鎖服務(wù)框架分布式系統(tǒng)

2022-05-27 08:18:00

HashMapHash哈希表

2019-09-25 09:17:43

物聯(lián)網(wǎng)技術(shù)信息安全

2024-03-26 00:00:06

RedisZSet排行榜

2024-07-05 11:01:13

2018-10-31 17:22:25

AI人工智能芯片

2023-12-01 08:39:29

分布式鎖系統(tǒng)

2019-10-31 09:48:53

MySQL數(shù)據(jù)庫事務(wù)

2019-10-08 10:37:46

設(shè)計(jì)技術(shù)程序員

2020-10-13 07:44:40

緩存雪崩 穿透

2024-02-28 08:59:47

2018-08-17 09:14:43

餓了么容器演進(jìn)

2021-07-13 12:21:34

PythonRPC通信

2021-04-09 10:03:12

大數(shù)據(jù)exactly-onc

2022-08-26 05:22:21

RabbitMQ架構(gòu)

2017-12-12 12:53:09

2017-03-10 21:04:04

Android適配
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)