分佈式鎖在不少應用場景下是很是有效的手段,好比當運行在多個機器上的不一樣進程須要訪問同一個競爭資源的時候,那麼就會涉及到進程對資源的加鎖和釋放,這樣才能保證數據的安全訪問。分佈式鎖實現的方案有不少,好比基於ZooKeeper實現、或者基於Mysql實現等等,今天咱們來一塊兒看看如何基於Redis實現分佈式鎖服務。redis
對於分佈式鎖的目標,咱們必須首先明確三點:算法
理解了上面咱們列出的三個點,咱們來分析一下通常的基於Redis實現的分佈式鎖:sql
使用Redis實現鎖最簡單的辦法是建立一個key,且這個key一般有有限的存活時間,這一點能夠利用Redis的過時時間特性,因此鎖最終會被釋放掉,當客戶端須要釋放資源的時候,客戶端delete這個key便可。安全
So far so good!可是有個單點問題,假如Redis master掛掉怎麼辦,所以咱們須要加個slave,當master掛掉的時候能夠切換到slave。這又帶來了新的問題,因爲Redis的複製是異步的,所以咱們不能保證同時只有一個客戶端得到鎖。dom
這個模型有很顯然的競態:異步
在特定條件下這種狀況是會發生的,當出現多個客戶端同時得到鎖的時候,咱們就認爲能夠這種鎖方案是不可靠的。分佈式
爲了後面更好的瞭解分佈式鎖的實現,咱們先來看看如何基於Redis單例實現鎖服務。咱們能夠用下面方法得到鎖:spa
SET resource_name my_random_value NX PX 30000
上面的命令在只有當key不存在的時候會執行成功(NX選項),同時會設置過時時間爲30000ms(PX選項)。key的值會被設置爲my_random_value。這個值在多個客戶端和鎖中必須是惟一的,咱們使用random value是爲了方便安全地釋放鎖,看看下面的腳本:code
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
只有當key存在且值是預期的值的時候纔會刪除key。這種方式能夠避免誤刪除其餘客戶端建立的鎖。例如,當客戶端獲取鎖以後執行一個很長時間的邏輯,一直過了鎖的過時時間,這個時候鎖會被自動釋放掉,而另一個客戶端又獲取了這個鎖,前一個客戶端終於執行完了邏輯執行,回頭釋放鎖,刪除key,其實這個時候釋放的已是另一個客戶端持有的鎖了。使用DEL是不安全的,由於客戶端有可能誤刪其餘客戶端持有的鎖。上面腳本的方法的好處是每次得到鎖的時候加上一個隨機的簽名,當釋放鎖的時候去看看是否是本身持有的鎖,這個時候就不會誤刪。 進程
如今咱們學會了如何在Redis單例上獲取鎖和釋放鎖,那麼接下來咱們看看如何在Redis集羣上獲取鎖和釋放鎖。
在分佈式環境下,假設咱們有N個master,這些節點都是獨立的,所以咱們沒有配置複製策略。上面咱們已經學會了如何在單機環境下獲取鎖和釋放鎖,咱們假設的更具體一些,N=5,爲了能獲取鎖,客戶端的步驟爲:
這篇文章主要介紹Redis實現分佈式鎖的基本方法,而後分別介紹經過Redis單例和Redis集羣實現分佈式鎖的方法。
《Redis官方文檔》