Redis鎖從面試連環炮聊到神仙打架。

背景鋪墊

面試的時候,不管你的簡歷寫沒寫 Redis,它基本上是一個繞不過的話題。

爲了引出本文要討論的關於 Redlock 的神仙打架的問題,我們就得先通過一個面試連環炮:

  1. Redis 做分佈式鎖的時候有需要注意的問題?

  2. 如果是 Redis 是單點部署的,會帶來什麼問題?

  3. 那你準備怎麼解決單點問題呢?

  4. 集羣模式下,比如主從模式,有沒有什麼問題呢?

  5. 你知道 Redis 是怎麼解決集羣模式也不靠譜的問題的嗎?

  6. 那你簡單的介紹一下 Redlock 吧?

  7. 你覺得 Redlock 有什麼問題呢?

很明顯,上面是一個常規的面試連環套路題。中間還可以插入很多其他的 Redis 的考察點,我這裏就不做擴展了。

單點的 Redis 做分佈式鎖不靠譜,導致了基於 Redis 集羣模式的分佈式鎖解決方案的出現。

基於 Redis 集羣模式的分佈式鎖解決方案還是不靠譜,Redis 的作者提出了 Redlock 的解決方案。

Redis 作者提出的 Redlock 的解決方案,另一位分佈式系統的大神覺得它不靠譜,於是他們之間開始了 battle。

基於這場 battle,又引發了更多的討論。

這場 battle 難分伯仲,沒有最後的贏家。如果一定要選出誰是最大的贏家的話,那一定是吃瓜網友。因爲對於吃瓜網友來說(比如我),可以從兩位大神的交鋒中學習到很多東西。

讓你深刻的體會到:看起來那麼無懈可擊的想法,細細推敲之下,並不是那麼天衣無縫。

所以本文就按照下面的五個模塊展開講述。

先來一波勸退:本文近1.2w字,謹慎觀看。看不下去不要緊,拉到最後點個“在看”。就是對於我最大的鼓勵。奧利給!

單點Redis

按照我的經驗,當面試聊到 Redis 的時候,百分之 90 的朋友都會說:Redis在我們的項目中是用來做熱點數據緩存的。

然後百分之百的面試官都會問:

Redis除了拿來做緩存,你還見過基於Redis的什麼用法?

接下來百分之 80 的朋友都會說到:我們還用 Redis 做過分佈式鎖。

(當然, Redis 除了緩存、分佈式鎖之外還有非常非常多的奇技淫巧,不是本文重點,大家有興趣的可以自己去了解一下。)

那麼面試官就會接着說:

那你給我描述(或者寫一下僞代碼)基於Redis的加鎖和釋放鎖的細節吧。

注意面試官這裏說的是加鎖和釋放鎖的細節,魔鬼都在細節裏。

問這個問題面試官無非是想要聽到下面幾個關鍵點:

關鍵點一:原子命令加鎖。因爲有的“年久失修”的文章中對於 Redis 的加鎖操作是先set key,再設置 key 的過期時間。這樣寫的根本原因是在早期的 Redis 版本中並不支持原子命令加鎖的操作。不是原子操作會帶來什麼問題,就不用我說了吧?如果你不知道,你先回去等通知吧。

而在 2.6.12 版本後,可以通過向 Redis 發送下面的命令,實現原子性的加鎖操作:

SET key random_value NX PX 30000

關鍵點二:設置值的時候,放的是random_value。而不是你隨便扔個“OK”進去。

先解釋一下上面的命令中的幾個參數的含義:

random_value:是由客戶端生成的一個隨機字符串,它要保證在足夠長的一段時間內在所有客戶端的所有獲取鎖的請求中都是唯一的。 

NX:表示只有當要設置的 key 值不存在的時候才能 set 成功。這保證了只有第一個請求的客戶端才能獲得鎖,而其它客戶端在鎖被釋放之前都無法獲得鎖。 

PX 30000:表示這個鎖有一個 30 秒的自動過期時間。當然,這裏 30 秒只是一個例子,客戶端可以選擇合適的過期時間。

再解釋一下爲什麼 value 需要設置爲一個隨機字符串。這也是第三個關鍵點。

關鍵點三:value 的值設置爲隨機數主要是爲了更安全的釋放鎖,釋放鎖的時候需要檢查 key 是否存在,且 key 對應的值是否和我指定的值一樣,是一樣的才能釋放鎖。所以可以看到這裏有獲取、判斷、刪除三個操作,爲了保障原子性,我們需要用 lua 腳本。

(基本上能答到這幾個關鍵點,面試官也就會進入下一個問題了。常規熱身送分題呀,朋友們,得記住了。)

集羣模式

面試官就會接着問了:

經過剛剛的討論,我們已經有較好的方法獲取鎖和釋放鎖。基於Redis單實例,假設這個單實例總是可用,這種方法已經足夠安全。如果這個Redis節點掛掉了呢?

到這個問題其實可以直接聊到 Redlock 了。但是你別慌啊,爲了展示你豐富的知識儲備(瘋狂的刷題準備),你得先自己聊一聊 Redis 的集羣,你可以這樣去說:

爲了避免節點掛掉導致的問題,我們可以採用Redis集羣的方法來實現Redis的高可用。

Redis集羣方式共有三種:主從模式,哨兵模式,cluster(集羣)模式

其中主從模式會保證數據在從節點還有一份,但是主節點掛了之後,需要手動把從節點切換爲主節點。它非常簡單,但是在實際的生產環境中是很少使用的。

哨兵模式就是主從模式的升級版,該模式下會對響應異常的主節點進行主觀下線或者客觀下線的操作,並進行主從切換。它可以保證高可用。

cluster (集羣)模式保證的是高併發,整個集羣分擔所有數據,不同的 key 會放到不同的 Redis 中。每個 Redis 對應一部分的槽。

(上面三種模式也是面試重點,可以說很多道道出來,由於不是本文重點就不詳細描述了。主要表達的意思是你得在面試的時候遇到相關問題,需要展示自己是知道這些東西的,都是面試的套路。)

在上面描述的集羣模式下還是會出現一個問題,由於節點之間是採用異步通信的方式。如果剛剛在 Master 節點上加了鎖,但是數據還沒被同步到 Salve。這時 Master 節點掛了,它上面的鎖就沒了,等新的 Master 出來後(主從模式的手動切換或者哨兵模式的一次 failover 的過程),就可以再次獲取同樣的鎖,出現一把鎖被拿到了兩次的場景。

鎖都被拿了兩次了,也就不滿足安全性了。一個安全的鎖,不管是不是分佈式的,在任意一個時刻,都只有一個客戶端持有。

Redlock簡介

爲了解決上面的問題,Redis 的作者提出了名爲 Redlock 的算法。

在 Redis 的分佈式環境中,我們假設有 N 個 Redis Master。這些節點完全互相獨立,不存在主從複製或者其他集羣協調機制。

前面已經描述了在單點 Redis 下,怎麼安全地獲取和釋放鎖,我們確保將在 N 個實例上使用此方法獲取和釋放鎖。

在下面的示例中,我們假設有 5 個完全獨立的 Redis Master 節點,他們分別運行在 5 臺服務器中,可以保證他們不會同時宕機。

從官網上我們可以知道,一個客戶端如果要獲得鎖,必須經過下面的五個步驟:

步驟描述來源:

http://redis.cn/topics/distlock.html

  1. 獲取當前 Unix 時間,以毫秒爲單位。

  2. 依次嘗試從 N 個實例,使用相同的 key 和隨機值獲取鎖。在步驟 2,當向 Redis 設置鎖時,客戶端應該設置一個網絡連接和響應超時時間,這個超時時間應該小於鎖的失效時間。例如你的鎖自動失效時間爲 10 秒,則超時時間應該在 5-50 毫秒之間。這樣可以避免服務器端 Redis 已經掛掉的情況下,客戶端還在死死地等待響應結果。如果服務器端沒有在規定時間內響應,客戶端應該儘快嘗試另外一個 Redis 實例。

  3. 客戶端使用當前時間減去開始獲取鎖時間(步驟 1 記錄的時間)就得到獲取鎖使用的時間。當且僅當從大多數(這裏是 3 個節點)的 Redis 節點都取到鎖,並且使用的時間小於鎖失效時間時,鎖纔算獲取成功。

  4. 如果取到了鎖,key 的真正有效時間等於有效時間減去獲取鎖所使用的時間(步驟 3 計算的結果)。

  5. 如果因爲某些原因,獲取鎖失敗(沒有在至少 N/2+1 個Redis實例取到鎖或者取鎖時間已經超過了有效時間),客戶端應該在所有的 Redis 實例上進行解鎖(即便某些 Redis 實例根本就沒有加鎖成功)。

通過上面的步驟我們可以知道,只要大多數的節點可以正常工作,就可以保證 Redlock 的正常工作。這樣就可以解決前面單點 Redis 的情況下我們討論的節點掛掉,由於異步通信,導致鎖失效的問題。

但是,還是不能解決故障重啓後帶來的鎖的安全性的問題。你想一下下面這個場景:

我們一共有 A、B、C 這三個節點。

  1. 客戶端 1 在 A,B 上加鎖成功。C 上加鎖失敗。

  2. 這時節點 B 崩潰重啓了,但是由於持久化策略導致客戶端 1 在 B 上的鎖沒有持久化下來。

  3. 客戶端 2 發起申請同一把鎖的操作,在 B,C 上加鎖成功。

  4. 這個時候就又出現同一把鎖,同時被客戶端 1 和客戶端 2 所持有了。

(接下來又得說一說Redis的持久化策略了,全是知識點啊,朋友們)

比如,Redis 的 AOF 持久化方式默認情況下是每秒寫一次磁盤,即 fsync 操作,因此最壞的情況下可能丟失 1 秒的數據。

當然,你也可以設置成每次修改數據都進行 fsync 操作(fsync=always),但這會嚴重降低 Redis 的性能,違反了它的設計理念。(我也沒見過這樣用的,可能還是見的太少了吧。)

而且,你以爲執行了 fsync 就不會丟失數據了?天真,真實的系統環境是複雜的,這都已經脫離 Redis 的範疇了。上升到服務器、系統問題了。

所以,根據墨菲定律,上面舉的例子:由於節點重啓引發的鎖失效問題,總是有可能出現的。

爲了解決這一問題,Redis 的作者又提出了延遲重啓(delayed restarts的概念。

意思就是說,一個節點崩潰後,不要立即重啓它,而是等待一定的時間後再重啓。等待的時間應該大於鎖的過期時間(TTL)。這樣做的目的是保證這個節點在重啓前所參與的鎖都過期。相當於把以前的帳勾銷之後才能參與後面的加鎖操作。

但是有個問題就是:在等待的時間內,這個節點是不對外工作的。那麼如果大多數節點都掛了,進入了等待。就會導致系統的不可用,因爲系統在TTL時間內任何鎖都將無法加鎖成功。

Redlock 算法還有一個需要注意的點是它的釋放鎖操作。

釋放鎖的時候是要向所有節點發起釋放鎖的操作的。這樣做的目的是爲了解決有可能在加鎖階段,這個節點收到加鎖請求了,也set成功了,但是由於返回給客戶端的響應包丟了,導致客戶端以爲沒有加鎖成功。所有,釋放鎖的時候要向所有節點發起釋放鎖的操作。

你可以覺得這不是常規操作嗎?

有的細節就是這樣,說出來後覺得不過如此,但是有可能自己就是想不到這個點,導致問題的出現,所以我們纔會說:細節,魔鬼都在細節裏。

好了,簡介大概就說到這裏,有興趣的朋友可以再去看看官網,補充一下。

中文:http://redis.cn/topics/distlock.html

英文:https://redis.io/topics/distlock

好了,經過這麼長,這麼長的鋪墊,我們終於可以進入到神仙打架環節。

神仙打架

神仙一:Redis 的作者 antirez 。有的朋友對英文名字不太敏感,所以後面我就叫他捲髮哥吧。

神仙二:分佈式領域專家 Martin Kleppmann,我們叫他長髮哥吧。

看完上面兩位神仙的照片,再看看我爲了寫這篇文章又日漸稀少的頭髮,我忍不住哭出聲來。可能只有給我點贊,才能平復我的心情吧。

捲髮哥在官網介紹 Redlock 頁面的最後寫到:如果你也是使用分佈式系統的人員,你的觀點和意見非常重要,歡迎和我們討論。

於是,“求錘得錘”!這一錘,錘出了衆多的吃瓜網友,其中不乏在相關領域的專業人士。

長髮哥出錘

故事得從 2016年2月8號 長髮哥發佈的一篇文章《How to do distributed locking》說起:

文章地址:

http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html

這一部分直接翻譯過來就是:

作爲本書(《數據密集型應用系統設計》)研究的一部分,我在Redis網站上 看到了一種稱爲Redlock的算法。該算法聲稱在Redis實現容錯的分佈式鎖(或更確切地說, 租約),並且該頁面要求來自分佈式系統人員的反饋。這個算法讓我產生了一些思考,因此我花了一些時間寫了我的這篇文章。

由於Redlock已經有10多個獨立的實現,而且我們不知道誰已經在依賴此算法,因此我認爲值得公開分享我的筆記。我不會討論Redis的其他方面,其中一些已經在其他地方受到了批評 。

你看這個文章,開頭就是火藥味十足:你說要反饋,那我就給你反饋。而且你這個東西有其他問題,我也就不說了。(其實作者在這篇文章中也說了,他很喜歡並且也在使用 Redis,只是他覺得這個 Redlock 算法是不嚴謹的)

長髮哥主要圍繞了下面的這張圖進行了展開:

要是一眼沒看明白,我再給你一箇中文版的,來自長髮哥於2017年出版的書《數據密集型應用系統設計》:

可以看到上面的圖片中提到了申請租約、租約到期的關鍵詞,租約其實就是可以理解爲帶超時時間的鎖。

而在書中,這張圖片的下面寫的描述這樣的,你咂摸咂摸:

拿 HBase 舉例,其設計的目標是確保存儲系統的文件一次只能由一個客戶端訪問,如果多個客戶端試圖同時寫入該文件,文件就會被破壞。那麼上面的圖片解釋起來就是:

  1. 客戶端 1 先去申請鎖,並且成功獲取到鎖。之後客戶端進行了長時間的 GC 導致了 STW 的情況。

  2. 在 STW 期間,客戶端 1 獲取的鎖的超時時間到了,鎖也就失效了。

  3. 由於客戶端 1 的鎖已經過期失效了,所以客戶端 2 去申請鎖就可以成功獲得鎖。

  4. 客戶端 2 開始寫文件,並完成文件的寫入。

  5. 客戶端 1 從 STW 中恢復過來,他並不知道自己的鎖過期了,還是會繼續執行文件寫入操作,導致客戶端 2 寫入的文件被破壞。而且可以看到,它沒有滿足鎖在任意時刻只有一個客戶端持有的原則,即沒有滿足互斥性。

書裏面沒有明說,但是你品一品,這裏的鎖服務難道不是在說 Redis?

有的朋友就會說了,那客戶端 1 寫入文件的時候,再判斷一下自己的鎖有沒有過期不就可以了嗎?

你可真是個小機靈鬼呢,那我問你,GC 可能是發生在任何時間的,萬一 GC 發生在判斷之後呢?

你繼續懟我,如果客戶端使用的是沒有 GC 的語言呢?

GC 不是導致線程暫停的唯一原因啊,朋友們。發生這種情況的原因有很多的,你看看長髮哥書裏舉的例子:

上面的內容總結起來,就是就算鎖服務是正常的,但是由於鎖是有持有時間的,由於客戶端阻塞、長時間的 GC 或者網絡原因,導致共享資源被一個以上的客戶端同時訪問了。

其實上面長髮哥在書裏直接說了:這是不正確的實現。

你多品一品,上面的圖是不是有點像由於 Redis 鎖的過期時間設置的不合理,導致前一個任務還沒執行完成,但是鎖的時間到期了,後一個任務也申請到了鎖。

對於這種場景,Redission 其實有自己的看門狗機制。但是不在這次 Redlock 的討論範圍內,所以這裏就不描述了。

長髮哥提出的解決方案是什麼呢?

他稱爲:fencing token。

長髮哥認爲使用鎖和租約機制來保護資源的併發訪問時,必須確保因爲異常原因,導致鎖過期的那個節點不能影響其他正常的部分,要實現這一目標,可以採用一直相當簡單的 fencing(柵欄)。

假設每次鎖服務在授予鎖或者租約時,還會同時返回一個 fencing 令牌,該令牌每次授予都會遞增。

然後,要求客戶端每次向存儲系統發送寫請求時,都必須包含所持有的 fencing 令牌。存儲系統需要對令牌進行校驗,發現如果已經處理過更高令牌的請求,則拒絕執行該請求。

比如下面的圖片:

  1. 客戶端 1 獲得一個具有超時時間的鎖的同時得到了令牌號 33,但隨後陷入了一個長時間的暫停直到鎖到期。

  2. 這時客戶端2已經獲得了鎖和令牌號 34 ,然後發送寫請求(以及令牌號 34 )到存儲服務。 

  3. 接下來客戶端 1 恢復過來,並以令牌號 33 來嘗試寫入,存儲服務器由於記錄了最近已經完成了更高令牌號(34 ),因此拒絕令牌號 33 的寫請求。

這種版本號的機制,讓我不禁想起了 Zookeeper。當使用 ZK 做鎖服務時,可以用事務標識 zxid 或節點版本 cversion 來充當 fencing 令牌,這兩個都可以滿足單調遞增的要求。

在長髮哥的這種機制中,實際上就是要求資源本身必須主動檢查請求所持令牌信息,如果發現已經處理過更高令牌的請求,要拒絕持有低令牌的所有寫請求。

但是,不是所有的資源都是數據庫裏面的數據,我們可以通過版本號去支持額外的令牌檢查的,那麼對於不支持額外的令牌檢查資源,我們也可以藉助這種思想繞過這個限制,比如對於訪問文件存儲服務的情況,我們可以將令牌嵌入到文件名中。

總之,爲了避免在鎖保護之外發生請求處理,需要進行額外的檢查機制。

長髮哥在書中也說到了:在服務端檢查令牌可能看起來有點複雜,但是這其實是推薦的正確的做法:系統服務不能假定所有的客戶端都表現的符合預期。從安全角度講,服務端必須防範這種來自客戶端的濫用。

這個就類似於我們作爲後端開發人員,也不能相信來自前端或者其他接口過來的數據,必須對其進行校驗。

到這裏長髮哥鋪墊完成了,開始轉頭指向 RedLock,他認爲 Redlock 是一個嚴重依賴系統時鐘的分佈式鎖。

他舉了一個例子:

  1. 客戶端 1 從 Redis 節點 A, B, C 成功獲取了鎖。由於網絡問題,無法訪問 D 和 E。

  2. 節點 C 上的時鐘發生了向前跳躍,導致它上面維護的鎖過期了。

  3. 客戶端 2 從 Redis 節點 C, D, E 成功獲取了同一個資源的鎖。由於網絡問題,無法訪問 A 和 B。

  4. 現在,客戶端 1 和客戶端 2 都認爲自己持有了鎖。

這樣的場景是可能出現的,因爲 Redlock 嚴重依賴系統時鐘,所以一旦系統的時間變得不準確了,那麼該算法的安全性也就得不到保障了。

長髮哥舉這個例子其實是爲了輔佐他前面提出的觀點:一個好的分佈式算法應該是基於異步模型的,算法的安全性不應該依賴與任何記時假設,就是不能把時間作爲安全保障的。在異步模型中,程序暫停、消息在網絡中延遲甚至丟失、系統時間錯誤這些因素都不應該影響它的安全性,只能影響到它的活性。

用大白話說,就是在極其極端的情況下,分佈式系統頂天了也就是在有限的時間內不能給出結果而已,而不能給出一個錯誤的結果。

這樣的算法實際上是存在的,比如 Paxos、Raft。很明顯,按照這個標準, Redlock 的安全級別是不夠的。

而對於捲髮哥提出的延遲啓動方案,長髮哥還是一棒子打死:你延遲啓動咋的?延遲啓動還不是依賴於合理準確的時間度量。

可能是長髮哥覺得舉這個時鐘跳躍的例子不夠好的,大家都可能認爲時鐘跳躍是不現實的,因爲對正確配置NTP就能擺正時鐘非常有信心。

在這種情況下,他舉了一個進程暫停可能導致算法失敗的示例:

  1. 客戶端 1 向 Redis 節點 A, B, C, D, E 發起鎖請求。

  2. 各個 Redis 節點已經把請求結果返回給了客戶端 1,但客戶端 1 在收到請求結果之前進入了長時間的 GC 階段。

  3. 長時間的 GC,導致在所有的 Redis 節點上,鎖過期了。

  4. 客戶端 2 在 A, B, C, D, E 上申請並獲取到了鎖。

  5. 客戶端 1 從 GC 階段中恢復,收到了前面第 2 步來自各個 Redis 節點的請求結果。客戶端 1 認爲自己成功獲取到了鎖。

  6. 客戶端 1 和客戶端 2 現在都認爲自己持有了鎖。

其實只要十分清楚 Redlock 的加鎖過程,我們就知道,這種情況其實對於 Redlock 是沒有影響的,因爲在第 5 步,客戶端 1 從 GC 階段中恢復過來以後,在 Redlock 算法中,(我們前面 Redlock 簡介的時候提到的第四步)如果取到了鎖,key 的真正有效時間等於有效時間減去獲取鎖所使用的時間。

所以客戶端1通過這個檢查發現鎖已經過期了,不會再認爲自己成功獲取到鎖了。

而隨後捲髮哥的回擊中也提到了這點。

但是,細細想來,我覺得長髮哥的意圖不在於此。拋開上面的問題來講,他更想突出的是,一個鎖在客戶端拿到後,還沒使用就過期了,這是不好的。從客戶端的角度來看,就是這玩意不靠譜啊,你給我一把鎖,我還沒用呢,你就過期了?

除了上面說的這些點外,長髮哥還提出了一個算是自己的經驗之談吧:

我們獲取鎖的用途是什麼?

在他看來不外乎兩個方面,效率和正確性。他分別描述如下:

如果是爲了效率,那麼就是要協調各個客戶端,避免他們做重複的工作。這種場景下,即使鎖偶爾失效了,只是可能出現兩個客戶端完成了同樣的工作,其結果是成本略有增加(您最終向 AWS 支付的費用比原本多5美分),或者帶來不便(例如,用戶最終兩次收到相同的電子郵件通知)。

如果是爲了正確性,那麼在任何情況下都不允許鎖失效的情況發生,因爲一旦發生,就可能意味着數據不一致,數據丟失,文件損壞,或者其它嚴重的問題。(比如個患者注射了兩倍的藥劑)

最後,長髮哥得出的結論是:neither fish nor fowl(不倫不類)

對於提升效率的場景下,使用分佈式鎖,允許鎖的偶爾失效,那麼使用單 Redis 節點的鎖方案就足夠了,簡單而且效率高。用 Redlock 太重。

對於正確性要求高的場景下,它是依賴於時間的,不是一個足夠強的算法。Redlock並沒有保住正確性。

那應該使用什麼技術呢?

長髮哥認爲,應該考慮類似 Zookeeper 的方案,或者支持事務的數據庫。

捲髮哥回擊

長髮哥發出《How to do distributed locking》這篇文章的第二天,捲髮哥就進行了回擊,發佈了名爲《Is Redlock safe?》的文章。

文章地址:http://antirez.com/news/101

要說大佬不愧是大佬,捲髮哥的回擊條理清楚,行文流暢。他總結後認爲長髮哥覺得 Redlock 不安全主要分爲兩個方面:

  1. 帶有自動過期功能的分佈式鎖,需要一種方法(fencing機制)來避免客戶端在過期時間後使用鎖時出現問題,從而對共享資源進行真正的互斥保護。馬丁說Redlock沒有這種機制。

  2. 馬丁說,無論問題“1”如何解決,該算法本質上都是不安全的,因爲它對系統模型進行了記時假設,而這些假設在實際系統中是無法保證的。

對於第一個點,捲髮哥列了5大點來反駁這個問題,其中一個重要的觀點是他認爲雖然 Redlock 沒有提供類似於fencing機制那樣的單調遞增的令牌,但是也有一個隨機串,把這個隨機串當做token,也可以達到同樣的效果啊。當需要和共享資源交互的時候,我們檢查一下這個token是否發生了變化,如果沒有再執行“獲取-修改-寫回”的操作。

最終得出的結論是一個靈魂反問:既然在鎖失效的情況下已經存在一種fencing機制能繼續保持資源的互斥訪問了,那爲什麼還要使用一個分佈式鎖並且還要求它提供那麼強的安全性保證呢?

然而第二個問題,對於網絡延遲或者 GC 暫停,我們前面分析過,對 Redlock 的安全性並不會產生影響,說明捲髮哥在設計的時候其實是考慮過時間因素帶來的問題的。

但是如果是長髮哥提出的時鐘發生跳躍,很明顯,捲髮哥知道如果時鐘發生跳躍, Redlock 的安全性就得不到保障,這是他的命門。

但是對於長髮哥寫時鐘跳躍的時候提出的兩個例子:

  1. 運維人員手動修改了系統時鐘。

  2. 從NTP服務收到了一個大的時鐘更新事件。

捲髮哥進行了回擊:

第一點這個運維人員手動修改時鐘,屬於人爲因素,這個我也沒辦法啊,人家就是要搞你,怎麼辦?加強管理,不要這樣做。

第二點從NTP服務收到一個大的時鐘更新,對於這個問題,需要通過運維來保證。需要將大的時間更新到服務器的時候,應當採取少量多次的方式。多次修改,每次更新時間儘量小。

關於這個地方的爭論,就看你是信長髮哥的時間一定會跳躍,還是信捲髮哥的時間跳躍我們也是可以處理的。

關於時鐘跳躍,有一篇文章可以看看,也是這次神仙打架導致的產物:

https://jvns.ca/blog/2016/02/09/til-clock-skew-exists/

文章得出的最終結論是:時鐘跳躍是存在的。

其實我們大家應該都經歷過時鐘跳躍的情況,你還記得2016年的最後一天,當時有個“閏秒”的概念嗎?導致2017年1月1日出現了07:59:60的奇觀。

打架的焦點

經過這樣的一來一回,其實雙方打架的焦點就很明確了,就是大延遲對分佈式鎖帶來的影響。


而對於大延遲給Redlock帶來的影響,就是長髮哥分析的那樣,鎖到期了,業務還沒執行完。捲髮哥認爲這種影響不單單針對 Redlock ,其他具有自動釋放鎖的分佈式鎖也是存在一樣的問題。

而關於大延遲的問題,我在某社交平臺上找到了兩位神仙的下面的對話:

捲髮哥問:我想知道,在我發文回覆之後,我們能否在一點上達成一致,就是大的消息延遲不會給Redlock的運行造成損害。

長髮哥答:對於客戶端和鎖服務器之間的消息延遲,我同意你的觀點。但客戶端和被訪問資源之間的延遲還是有問題的。

所以通過捲髮哥的回擊文章和某社交平臺的記錄,他是同意大的系統時鐘跳躍會造成 Redlock 失效的。在這一點上,他與長髮哥的觀點的不同在於,他認爲在實際系統中是可以通過好的運維方式避免大的時鐘跳躍的。

所以到這裏,兩位神仙好像又達到了一個平衡,實現了爭論上的求同存異。

打架總結


3

推薦閱讀


後端程序員必備:書寫高質量SQL的30條建議

優化if-else代碼的八種方案

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

-HashMap

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