對Redis分佈式鎖的一點錯誤理解

Redis 分佈式鎖在平常工做中常常用到,面試中也是高頻問題,本身在看的時候,發現對於 Redlock 理解有些誤差,主要是 Redlock 是在單實例(單集羣)仍是多實例(多集羣)下實現的。查資料的時候發現有這個問題的毫不止我一個,甚至不少人理解都是錯的還寫博客告訴別人。固然若是你已經知道了,看看我走過的彎路或許也會有些收穫。面試

Redis 分佈式鎖的實現

在介紹 Redlock 以前,先看下通常狀況下的實現方式。redis

加鎖:算法

SET resource_name my_random_value NX PX 30000
複製代碼

解鎖:緩存

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
複製代碼

這是一段 lua 腳本,意思是:當 key 存在而且值剛好等於給定的值時,才刪除 keybash

這裏有幾個須要注意的點dom

  • 用 SET 命令就夠了,SETNX 和 EXPIRE 組合使用沒法保證原子性。
  • my_random_value,惟一值,配合解鎖的 lua 腳本看,用於刪除時確保只有加鎖成功的客戶端才能釋放鎖。
  • NX:只在 key 不存在時,才設置。不能變
  • PX:過時時間,單位爲毫秒。若是改成 EX,單位變爲秒。這兩個選項沒法同時存在。

若是是面試,當你說出是如何實現的,面試官可能就會問這種實現有什麼問題。異步

Redis 分佈式鎖的問題

首先若是是單節點,節點一掛,鎖就沒了,等節點再重啓,別的客戶端就能夠獲取到鎖,也就是兩個客戶端都有鎖了,這就有問題了。分佈式

能夠添加一個 slave 節點,當 master 節點掛了能夠切換到 slave,可是還會有問題,由於 Redis 默認是異步進行主從同步的,好比如下場景:lua

  1. 客戶端 A 獲取 master 中的鎖。
  2. 在將 key 的寫入傳輸到 slave 以前,master 崩潰。
  3. slave 晉升爲 master
  4. 客戶端 B 獲取了客戶端 A 已經獲取到的鎖。

如何解決這種問題呢,Redis 官網有一種算法實現,Redlock。spa

Redlock

加鎖的過程

  1. 客戶端獲取當前時間,單位毫秒
  2. 嘗試依次獲取 N 個實例中的鎖,在全部實例中使用相同的 key 和隨機值。在每一個實例中設置 key 的時候,客戶端會設置一個超時時間,這個超時時間應該小於鎖的有效時間,以防節點已經掛掉。例如鎖有效時間爲 10 秒,則超時時間應該在 5-50 毫秒之間。
  3. 客戶端經過從當前時間中減去在步驟 1 中得到的時間戳,來計算獲取鎖所花費的時間。當且僅當客戶端可以在大多數實例中得到鎖(超過一半)且獲取鎖所花費的總時間少於鎖的有效時間,才認爲已經獲取到鎖。
  4. 若是獲取了鎖,則將其有效時間視爲初始有效時間減去獲取鎖所使用的時間(步驟 3 計算的結果)。實例中每一個 key 的過時時間是初始有效時間,而這一步計算的有效時間只是用於給客戶端使用,並不會設置到 redis 中。
  5. 若是客戶端因爲某種緣由(沒法鎖定 N / 2 + 1 個實例或有效時間爲負)而未能得到該鎖,則它將嘗試解鎖全部實例(即使某些 Redis 實例根本就沒有加鎖成功,防止某些節點獲取到鎖可是客戶端沒有獲得響應而致使接下來的一段時間不能被從新獲取鎖)。

問題就在於,對於 N 個實例的理解,我最開始覺得能夠是集羣中的多個 master 節點,可是問題就來了,如何在同一個集羣中多個 master 設置相同的 key。

走了不少彎路,最後發現是多個實例(多集羣),也就是這些實例之間並無任何關係,這樣也能夠理解爲何能夠在每一個實例設置相同的 key 以及爲何控制多數加鎖成功的判斷須要放在客戶端了。可是在實際場景中就不大可能會使用,由於實現 Redlock 最少須要三個節點,生產環境基本都是集羣,也就是爲了加鎖就須要至少鏈接 3 個集羣。什麼狀況下一個服務會連多個 Redis 集羣呢,多級緩存?每一個 Redis 是不一樣業務的緩存?我還沒了解過。

若是這是面試官期待的基於 Redis 分佈式加鎖解決方案,我以爲這個面試官可能也理解的有點問題。

奇怪的知識

走彎路的時候也瞭解了很多新的知識

redis-cli -c 參數

幫助中對於 -c 是這麼描述的

Enable cluster mode (follow -ASK and -MOVED redirections).
複製代碼

雖說是啓用集羣模式,但並非服務端分單機模式仍是集羣模式,只是加了 -c 會自動進行重定向。

開始沒加 -c 去請求一個不在本節點存儲的 key ,會返回重定向信息,而若是加上,會自動進行重定向。

key 所屬的哈希槽的計算

集羣狀況下,key 屬於哪一個哈希槽是在客戶端計算的仍是服務端?

服務端確定是有的,若是隻在客戶端進行,那就至關於加了一層查詢路由,一致性須要由這一層保證,而在服務端沒有任何保證,這確定是不對的。並且像上面給出的重定向信息,說明服務端確定是計算過的。

客戶端通常也會有,只是看成緩存,每次直接選擇存放 key 的節點,而不須要頻繁的進行重定向。

通常若是是集羣的話,最開始客戶端就會創建整個集羣的拓撲,獲取每一個節點保存那些槽位,可是後續若是節點和槽位的映射關係變化了,具體的刷新策略就看每一個客戶端的實現了。

相關文章
相關標籤/搜索