nodejs學習筆記——基于 Redis 的分布式鎖

前言

關于redis的分布式鎖,redis官方引出了一個算法,命名為redlock。
同時,提供了各類的實現(xiàn)可供使用,例如Redlock-rb for Ruby、Redlock-py for Python、Redisson for Java等。
因此,深入了解Redis分布鎖的運用同時分析下node-redlock


基本特性

概括了三個特性,利用這三個特性,從最小程度上去約束分布鎖。

  1. 互斥。在任何場景下,只有一個用戶可以持有鎖。
  2. 死鎖釋放。最終一個鎖總是會被回收,即便持有鎖的用戶崩潰或是被隔離。
  3. 錯誤容忍。只要大部分的Redis節(jié)點在運行,用戶就可以獲取和釋放鎖。
“為什么基于故障切換的實現(xiàn)是不夠的?”

使用Redis鎖住一個資源最簡單的方式是,生成一個限時的key,使用Redis 的expire特征,所以最終這個key會被釋放(以上的特征2)當用戶需要釋放資源時,釋放key。
表面上還不錯,但是存在一個問題:如果master掛了怎么辦?那添加一個slave。很不幸這不可行,違背了以上的特征1,因為Redis的復制是異步進行的。
范例:用戶A在master持有key,master在把key傳輸給slave前掛了,slave被提升為master,然后用戶B又獲得了一份A早已鎖住的資源的key。SAFETY VIOLATION!

因此redis提供了SET resource_name my_random_value NX PX 30000這種方式去設置鎖。
NX:只在鍵不存在時, 才對鍵進行設置操作
PX milliseconds:將鍵的過期時間設為xx毫秒

// 通過該語句判斷只有key存在并且是預期的那個才釋放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

避免誤刪別的用戶的key是很重要的。

Redlock算法

為了獲取key用戶會進行以下的操作:

  1. 獲取當前的毫秒數(shù)
  2. 嘗試去陸續(xù)獲得所有設備的鎖,使用相同的key名稱和隨機的value。 在這個階段,當設置鎖的時候會有一個相比auto-release時間小的延遲。這樣可以避免用戶在Redis節(jié)點掛了之后任然嘗試去訪問:如果一個實例不可用,我們應該盡快轉移到下一個。
  3. 用戶可以算出加鎖花費了多少時間。只有用戶可以獲得大部分設備的鎖,并且其中花費的總時間少于鎖設置的有效時間,該鎖可以考慮被加上。
  4. 鎖的有效時間 = 初始有效時間 - 加鎖消耗時間
  5. 假如用戶加鎖失敗,無法鎖 N/2+1 的設備或者是設置的有效時間非法,其將會嘗試解鎖所有設備,包括那些不能鎖的
系統(tǒng)活性參數(shù)(Liveness arguments)

基于以下三個特征:

  1. 鎖自動釋放(keys過期之后): 最終可以再次被上鎖。
  2. 用戶們若沒有成功加鎖,或者是加鎖之后工作中斷了,會共同釋放鎖。盡量讓用戶重新獲得鎖的時候不必等待keys釋放。
  3. 事實上當用戶需要重新嘗試上鎖,很顯然需要的時間比獲得大部分鎖的時間多的多,從而使資源競爭時分腦情況(split brain)不那么容易發(fā)生。

了解了Redis分布式的實現(xiàn)以后,其實覺得大多數(shù)的分布式系統(tǒng)其實原理很簡單,但是為了保證分布式系統(tǒng)的可靠性需要注意很多的細節(jié),瑣碎異常,極端情況。


引申內容

我們要鎖用來干什么?

  1. 提升效率,用鎖來保證一個任務沒有必要被執(zhí)行兩次。(比如很昂貴的計算)
  2. 保證正確,使用鎖來保證任務按照正常的步驟執(zhí)行,防止兩個節(jié)點同時操作一份數(shù)據(jù),造成文件沖突,數(shù)據(jù)丟失。

對于第一種原因,我們對鎖是有一定寬容度的,就算發(fā)生了兩個節(jié)點同時工作,對系統(tǒng)的影響也僅僅是多付出了一些計算的成本,沒什么額外的影響。這個時候 使用單點的 Redis 就能很好的解決問題,沒有必要使用RedLock,維護那么多的Redis實例,提升系統(tǒng)的維護成本。

對于第二種原因,對正確性嚴格要求的場景(比如訂單,或者消費),就算使用了 RedLock 算法仍然不能保證鎖的正確性。

好的分布式系統(tǒng)應當是異步的,且不能時間作為安全保障的。因為在分布式系統(tǒng)中有會程序暫停,網絡延遲,系統(tǒng)時間錯誤,這些因數(shù)都不能影響分布式系統(tǒng)的安全性,只能影響系統(tǒng)的活性(liveness property)。換句話說,就是在極端情況下,分布式系統(tǒng)頂多在有限的時間內不能給出結果,但是不能給出錯誤的結果。

因此Martin提出對Redlock的批評:對于提升效率的場景下,RedLock 太重。對于對正確性要求極高的場景下,RedLock 并不能保證正確性。

Redlock的作者同時也是Redis的作者 antirez 回答了這兩個問題。
對于第一個問題其他具有自動釋放鎖的分布式鎖都沒辦解決這個問題。
第二個問題,主要考慮系統(tǒng)暫停,網絡延遲,還有就是系統(tǒng)的時間發(fā)生階躍。RedLock做了一些微小的工作,但是沒辦法完全避免,其他帶有自動釋放的分布式鎖也沒有辦法。至于系統(tǒng)時間發(fā)生階躍,因為RedLock建立在了 Time 是可信的模型上,理論上 Time 也是會發(fā)生錯誤,但是在現(xiàn)實中,良好的運維和工程一些機制是可以最大限度的保證 Time 可信。并不是說單靠Redlock就能解決全部的問題。

最后Martin 推薦使用ZooKeeper 實現(xiàn)分布事務鎖。Zookeeper 和 Redis的鎖有什么區(qū)別? Zookeeper解決了Redis沒有解決的問題了么?之后我們再研究。


node-redlock

其實這個沒有太多值得分析的內容。node-redlock的代碼不多,本質也就是一個js文件。核心的加鎖釋放依舊是通過redlock的lua腳本,通過Promise去處理多個異步事務,lock存放在array中等等。


心得

Redis鎖的原理其實并不復雜,甚至封裝好了方法,使用時不需要管內部的處理流程。但是為了保證分布式系統(tǒng)的可靠性,還是盡可能多的了解其中的細節(jié)。
redis的分布鎖真的完美嗎?推薦完整閱讀Redis RedLock 完美的分布式鎖么?或者是直接查看里面的英文原文。


參考

redis的分布鎖
node-redlock倉庫
NodeJS 中基于 Redis 的分布式鎖解決方案。
Redis RedLock 完美的分布式鎖么?

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容