首頁 >後端開發 >php教程 >詳解Redis實現分散式鎖

詳解Redis實現分散式鎖

小云云
小云云原創
2017-12-14 14:42:412483瀏覽

之前我們使用的定時任務都是只部署在了單台機器上,為了解決單點的問題,為了保證一個任務,只被一台機器執行,就需要考慮鎖的問題,於是就花時間研究了這個問題。到底要怎樣實現一個分散式鎖呢?本文主要介紹了Redis實現分散式鎖的方法範例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考,希望能幫助到大家。

鎖的本質就是互斥,保證任何時候能有一個客戶端持有同一個鎖,如果考慮使用redis來實現一個分散式鎖,最簡單的方案就是在實例裡面創建一個鍵值,釋放鎖的時候,將鍵值刪除。但是一個可靠且完善的分散式鎖需要考慮的細節比較多,我們就來看看如何寫一個正確的分散式鎖。

單一版分散式鎖定 SETNX

所以我們直接基於 redis 的 setNX (SET if Not eXists)指令,實作一個簡單的鎖定。直接上偽碼

鎖定的取得:

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

幾個細節要注意:

首先在取得鎖定的時候我們需要設定設定逾時時間。設定超時時間是為了,防止客戶端崩潰,或是網路出現問題以後鎖一直被持有。真個系統就死鎖了。

使用setNX 指令,保證查詢和寫入兩個步驟是原子的

在鎖釋放的時候我們判斷了KEYS[1]) == ARGV[1],在這裡KEYS [1]是從redis裡面取出的value,ARGV[1]是上文產生的my_random_value。之所以進行以上的判斷,是為了確保鎖被鎖的持有者釋放。我們假設不進行這一步驟校驗:

  1. 客戶端A取得鎖,後發執行緒掛起了。時間大於鎖的過期時間。

  2. 鎖定過期後,客戶端B取得鎖定。

  3. 客戶端A恢復以後,處理完相關事件,向redis啟動 del指令。鎖被釋放

  4. 客戶端C取得鎖定。這個時候一個系統中同時兩個客戶端持有鎖。

造成這個問題的關鍵,在於客戶端B所持有的鎖,被客戶端A釋放了。

鎖的釋放必須使用lua腳本,確保操作的原子性。鎖的釋放包含了get,判斷,del三個步驟。如果不能保證三個步驟的原子性,分散式鎖就會有並發問題。

注意了以上細節,一個單一redis節點的分散式鎖定就達成了。

在這個分散式鎖定中還是存在一個單點的redis。也許你會說,Redis是 master-slave的架構,故障的時候切換到slave就好,但Redis的複製是異步的。

  1. 如果在客戶端A在master上拿到了鎖。

  2. 在master將資料同步到slave上之前,master宕機。

  3. 客戶端B就從slave上又一次拿到了鎖。

這樣由於Master的宕機,造成了同時多人持有鎖。如果你的系統可用接受短時間內,有多人持有鎖。這個簡單的方案就能解決問題。

但是如果解決這個問題。 Redis的官方提供了一個Redlock的解決方案。

RedLock 的實作

為了解決,Redis單點的問題。 Redis的作者提出了RedLock的解決方案。方案非常的巧妙和簡潔。

RedLock的核心思想就是,同時使用多個Redis Master來冗餘,而這些節點都是完全的獨立的,也不需要對這些節點之間的資料進行同步。

假設我們有N個Redis節點,N應該是一個大於2的奇數。 RedLock的實作步驟:

  1. 取得目前時間

  2. #使用上文提到的方法依序取得N個節點的Redis鎖定。

  3. 如果取得到的鎖的數量大於(N/2+1)個,且取得的時間小於鎖定的有效時間(lock validity time)就認為取得到了一個有效的鎖。鎖自動釋放時間就是最初的鎖釋放時間減去先前取得鎖所消耗的時間。

  4. 如果取得鎖的數量小於(N/2+1),或在鎖的有效時間(lock validity time)內沒有取得到足夠的說,就認為取得鎖定失敗。這個時候需要向所有節點發送釋放鎖的訊息。

對於釋放鎖定的實作就很簡單了。想所有的Redis節點發起釋放的操作,無論之前是否取得鎖定成功。

同時需要注意幾個細節:

重試取得鎖定的間隔時間應為隨機範圍而非固定時間。這樣可以防止,多客戶端同時一起向Redis叢集發送獲取鎖的操作,避免同時競爭。同時取得相同數量鎖的情況。 (雖然機率很低)

如果某master節點故障之後,回覆的時間間隔應大於鎖的有效時間。

  1. 假設有A,B,C三個Redis節點。

  2. 客戶端foo取得到了A、B兩個鎖定。

  3. 這時候B宕機,所有記憶體的資料遺失。

  4. B節點回覆。

  5. 這時候客戶端bar重新取得鎖,取得到B,C兩個節點。

  6. 此時又有兩個客戶端取得到鎖了。

所以如果恢復的時間將大於鎖的有效時間,就可以避免以上情況發生。同時如果效能要求不高,甚至可以開啟Redis的持久化選項。

總結

了解了Redis分散式的實作以後,其實覺得大多數的分散式系統其實原理很簡單,但為了確保分散式系統的可靠性需要注意很多的細節,瑣碎異常。

RedLock演算法實現的分散式鎖定就是簡單高效,思路相當巧妙。

但是RedLock就一定安全麼?我也會寫一篇文章來討論這個問題。敬請大家期待。

相關推薦:

詳解redisson實作分散式鎖定方法原理

#php redis分散式鎖定與任務佇列程式碼實例詳解

分散式鎖定的多種實作方式

#

以上是詳解Redis實現分散式鎖的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn