在許多環境中,分佈式鎖是一種很是有用的原語,其中不一樣的進程必須以互斥的方式與共享資源一塊兒運行。php
有許多庫和博客文章描述瞭如何使用Redis實現DLM(分佈式鎖管理器),可是每一個庫都使用不一樣的方法,並且許多庫使用的是一種簡單的方法,與稍微複雜的設計相比,能夠得到較低的保障。node
此頁面試圖提供一種更典型的算法來使用Redis實現分佈式鎖,咱們提出了一種稱爲Redlock的算法,它實現了一種咱們認爲比vanilla單實例方法更安全的DLM,咱們但願社區將對其進行分析,提供反饋,並將其做爲實施或更復雜或替代設計的起點。git
在描述算法以前,這裏有幾個已經可用的實現的連接,可用於參考。github
咱們將僅使用三個屬性對咱們的設計進行建模,從咱們的角度來看,這些屬性是以有效方式使用分佈式鎖所需的最低保障。面試
爲了理解咱們想要改進的內容,讓咱們分析大多數基於Redis的分佈式鎖庫的當前狀態。redis
使用Redis鎖定資源的最簡單方法是在實例中建立密鑰,密鑰一般使用Redis過時功能在有限的生存時間內建立,所以最終它將被釋放(咱們列表中的屬性2),當客戶端須要釋放資源時,它會刪除密鑰。算法
從表面上看,這頗有效,但存在一個問題:這是咱們架構中的單點故障,若是Redis主機出現故障會怎樣?好吧,讓咱們添加一個從機!若是主服務器不可用,則使用它,遺憾的是,這不可行。經過這樣作,咱們沒法實現互斥的安全屬性,由於Redis主從複製是異步的。安全
這種模式存在明顯的競爭條件:服務器
有時在特殊狀況下,例如在故障期間,多個客戶端能夠同時保持鎖定,這是徹底正常的,若是是這種狀況,你可使用基於主從複製的解決方案,不然,咱們建議實施本文檔中描述的解決方案。架構
在嘗試克服上述單實例設置的限制以前,讓咱們看看在這個簡單的例子中如何正確地作到這一點,由於在時常能夠接收競爭條件的應用中,這其實是一個可行的解決方案,由於鎖定到單個實例是咱們將用於此處描述的分佈式算法的基礎。
要得到鎖,可採起的方法以下:
SET resource_name my_random_value NX PX 30000
該命令僅在密鑰尚不存在時才設置密鑰(NX選項),到期時間爲30000毫秒(PX選項),鍵的值設置爲「myrandomvalue」,此值必須在全部客戶端和全部鎖定請求中都是惟一的,基本上使用隨機值以便以安全的方式釋放鎖,使用一個告訴Redis的腳本:只有當密鑰存在而且密鑰中存儲的值正是我指望的那個時才刪除密鑰,這是經過如下Lua腳本完成的:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end