技術分享 | Redis 之分散式鎖

ITPUB社群發表於2023-01-04

作者:賁紹華

愛可生研發中心工程師,負責專案的需求與維護工作。其他身份:柯基鏟屎官。

本文來源:原創投稿

*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。


引言:之前的一篇《快取穿透 - Redis Module之布隆過濾器》中,介紹了布隆過濾器的使用,本篇主要透過實際業務場景來講述 Redis 中關於分散式鎖與 Red lock 的相關內容。

一、什麼是分散式鎖

分散式鎖指的就是分散式系統下使用的鎖(說了好像等於沒說),在分散式系統中,常常需要協調元件間的動作。

如果不同的系統或是同一個系統的不同主機之間共享了一個或一組資源,那麼訪問這些資源的時候,往往需要互斥來防止彼此干擾來保證一致性,這個時候,便需要使用到分散式鎖。

技術分享 | Redis 之分散式鎖

二、場景案例

舉個例子,假設ATM機A、ATM機B同時對同一賬戶入賬,它們是兩個獨立且相同的業務系統。由於餘額是要在現有金額上進行增加的,中間的前後操作會出現時差。
則A或B增加餘額時都需要先獲取互斥鎖,鎖住需要操作的資源,增加餘額後釋放鎖。

三、使用Redis實現分散式鎖

3.1 帶TTL的key

在 Redis 中建立一個 key ,這個 key 有一個失效時間(TTL),以保證鎖最終會被自動釋放掉(這個對應上邊腦圖的活性A)

即:

get->不存在,獲取成功->set->ttl->del
當客戶端釋放資源(解鎖)時,會刪除掉這個 key 。

3.2 setNX

使用3.1的方式存在一個問題,那就是每次都得先 get 一下看看這個 key 是否存在,插入之後還需要再設定 TTL 。
非常的繁瑣且 get->set->ttl 操作並不是原子性的,需要額外處理類似 get 不存在但 set 又存在、鎖被其他客戶端釋放掉的場景。
Redis 提供了 setNX 命令(如果不存在就插入,並設定 key 的超時時間。如果存在則什麼也不做)
使用命令:
set key value px milliseconds nx

3.3 setNX + Lua

在3.2中還存在一個問題,例如:

  1. ATM-A 獲取鎖成功
  2. ATM-A 操作中,持續阻塞
  3. 設定 key 過期了,鎖被自動釋放
  4. ATM-B 獲取到鎖
  5. ATM-A 操作此時恢復處理,操作完成開始釋放鎖
  6. ATM-B 持有的鎖被 ATM-A 釋放掉了
為了解決這個問題,釋放鎖之前需要對比 value 值(需要保證值的唯一性),釋放鎖的時候只有 key 存在並且儲存的值和當前客戶端指定的值一樣才能刪除成功。

同樣的,get->delete 操作並不是原子性的,需要使用 lua 指令碼來同時完成:

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

3.4 鎖的續期

鎖的過期時間是插入 key 時直接設定的一個大概時間區間,實際業務執行過程中不能精確預估具體的執行的時間。會出現客戶端正在處理時 key TTL 過期導致的被提前釋放問題。
解決方式:可以讓獲得鎖的客戶端開啟一個守護程式,用於給快要過期的key增加超時時間。當業務執行完成時,再主動關閉該守護程式。

3.5 高可用問題

單節點 Redis 例項會導致獲取鎖失敗,業務直接停擺。但想透過增加 slave 節點解決這個問題其實是行不通的,因為 Redis 的主從同步通常是非同步的。

會出現以下的問題:

  1. ATM-A 從 Redis master 節點獲取到鎖
  2. 在 master 將鎖同步到 slave 之前,master 當機
  3. slave 節點被晉級為 master 節點
  4. ATM-B 獲取到了鎖,ATM-A 業務還在進行中,導致安全失效

四、Redis Module - RedLock

RedLock 正是為了防止單點故障而設計的基於 Redis 的分散式鎖實現。
它是由N(大於等於3的奇數個)個 Redis master 節點組成的,節點與節點之間不使用複製或任何隱式協調系統。
當客戶端需要獲取鎖時,會嘗試順序從N個例項中獲取,在所有例項中使用相同的key與value。
  • 官方文件:
  • 使用方式:

4.1 獲取鎖:

當 N / 2 + 1 個節點獲取到鎖時則成功得到鎖(因為如果小於一半判斷為成功的話,有可能出現多個客戶端都成功獲取鎖的情況, 從而使鎖失效)

4.2 釋放鎖:

客戶端應該向所有 Redis 節點發起釋放鎖的操作。即使當時向某個節點獲取鎖沒有成功,在釋放鎖的時候也不應該漏掉這個節點

4.3 延遲重啟:

一個節點崩潰後,先不立即重啟它,而是等待一段時間再重啟,這段時間應該大於鎖的有效時間(lock validity time)。
這樣的話,這個節點在重啟前所參與的鎖都會過期,它在重啟後就不會對現有的鎖造成影響。

4.4 優點:

  • 有效防止單點故障

4.5 缺點:

  • 需要維護多臺 Redis Master ,使用起來相當笨重
  • RedLock 演算法對時鐘依賴性強,若叢集中的某個節點發生時鐘異常問題,可能會因此而引發鎖安全性問題
  • 如果有節點發生崩潰重啟,還是會對鎖的安全性有影響的。具體的影響程度跟 Redis 對資料的持久化程度有關
技術分享 | Redis 之分散式鎖
本文關鍵字:#分散式鎖# #Redis Redlock#

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2930744/,如需轉載,請註明出處,否則將追究法律責任。

相關文章