作者:小林coding
計算機八股文網站:https://xiaolincoding.com
哈嘍,我是小林。
今天跟大家聊聊兩個問題:
分散式鎖是用於分散式環境下並行控制的一種機制,用於控制某個資源在同一時刻只能被一個應用所使用。如下圖所示:
Redis 本身可以被多個使用者端共用存取,正好就是一個共用儲存系統,可以用來儲存分散式鎖,而且 Redis 的讀寫效能高,可以應對高並行的鎖操作場景。
Redis 的 SET 命令有個 NX 引數可以實現「key不存在才插入」,所以可以用它來實現分散式鎖:
基於 Redis 節點實現分散式鎖時,對於加鎖操作,我們需要滿足三個條件。
滿足這三個條件的分散式命令如下:
SET lock_key unique_value NX PX 10000
而解鎖的過程就是將 lock_key 鍵刪除(del lock_key),但不能亂刪,要保證執行操作的使用者端就是加鎖的使用者端。所以,解鎖的時候,我們要先判斷鎖的 unique_value 是否為加鎖使用者端,是的話,才將 lock_key 鍵刪除。
可以看到,解鎖是有兩個操作,這時就需要 Lua 指令碼來保證解鎖的原子性,因為 Redis 在執行 Lua 指令碼時,可以以原子性的方式執行,保證了鎖釋放操作的原子性。
// 釋放鎖時,先比較 unique_value 是否相等,避免鎖的誤釋放
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
這樣一來,就通過使用 SET 命令和 Lua 指令碼在 Redis 單節點上完成了分散式鎖的加鎖和解鎖。
基於 Redis 實現分散式鎖有什麼優缺點?
基於 Redis 實現分散式鎖的優點:
基於 Redis 實現分散式鎖的缺點:
超時時間不好設定。如果鎖的超時時間設定過長,會影響效能,如果設定的超時時間過短會保護不到共用資源。比如在有些場景中,一個執行緒 A 獲取到了鎖之後,由於業務程式碼執行時間可能比較長,導致超過了鎖的超時時間,自動失效,注意 A 執行緒沒執行完,後續執行緒 B 又意外的持有了鎖,意味著可以操作共用資源,那麼兩個執行緒之間的共用資源就沒辦法進行保護了。
Redis 主從複製模式中的資料是非同步複製的,這樣導致分散式鎖的不可靠性。如果在 Redis 主節點獲取到鎖後,在沒有同步到其他節點時,Redis 主節點宕機了,此時新的 Redis 主節點依然可以獲取鎖,所以多個應用服務就可以同時獲取到鎖。
為了保證叢集環境下分散式鎖的可靠性,Redis 官方已經設計了一個分散式鎖演演算法 Redlock(紅鎖)。
它是基於多個 Redis 節點的分散式鎖,即使有節點發生了故障,鎖變數仍然是存在的,使用者端還是可以完成鎖操作。
Redlock 演演算法的基本思路,是讓使用者端和多個獨立的 Redis 節點依次請求申請加鎖,如果使用者端能夠和半數以上的節點成功地完成加鎖操作,那麼我們就認為,使用者端成功地獲得分散式鎖,否則加鎖失敗。
這樣一來,即使有某個 Redis 節點發生故障,因為鎖的資料在其他節點上也有儲存,所以使用者端仍然可以正常地進行鎖操作,鎖的資料也不會丟失。
Redlock 演演算法加鎖三個過程:
第一步是,使用者端獲取當前時間。
第二步是,使用者端按順序依次向 N 個 Redis 節點執行加鎖操作:
第三步是,一旦使用者端完成了和所有 Redis 節點的加鎖操作,使用者端就要計算整個加鎖過程的總耗時(t1)。
加鎖成功要同時滿足兩個條件(簡述:如果有超過半數的 Redis 節點成功的獲取到了鎖,並且總耗時沒有超過鎖的有效時間,那麼就是加鎖成功):
加鎖成功後,使用者端需要重新計算這把鎖的有效時間,計算的結果是「鎖的最初有效時間」減去「使用者端為獲取鎖的總耗時(t1)」。
加鎖失敗後,使用者端向所有 Redis 節點發起釋放鎖的操作,釋放鎖的操作和在單節點上釋放鎖的操作一樣,只要執行釋放鎖的 Lua 指令碼就可以了。
系列《圖解Redis》文章:
面試篇:
資料型別篇:
持久化篇:
功能篇:
高可用篇:
快取篇: