分布式鎖的選擇與應用

2021-09-28 18:34:08 字數 2903 閱讀 7190

目前業界使用分布式鎖通常是依賴於redis或zookeeper,下面我們就以這兩種方式分析比較下優劣。

加鎖方式:set resource_name my_random_value nx px 10000

解鎖方式:為了防止誤解鎖,因此借助lua指令碼實現

if redis.call("get",keys[1]) == ar**[1] then

return redis.call("del",keys[1])

else

return 0

end

分析:這裡就可以看出來為什麼設定鎖的時候要給乙個隨機的value,因為解鎖的時候需要比對value值和預期值是否一致,因此避免誤解鎖。

為什麼使用lua指令碼來解鎖呢,因為lua指令碼可以保證原子性,如果是客戶端操作api解鎖我們看下圖:

這個方案存在乙個問題就是鎖續期的問題,試想一下客戶端處理業務流程超過了鎖的自動釋放時間,那麼別的客戶端就可以繼續獲取鎖,此時系統中存在多個客戶端持有鎖,這樣共享資源將不在安全。

readlock演算法流程:

假設我們有n個master節點,官方文件裡將n設定成5,其實大等於3就行。

獲取當前時間(單位是毫秒)

輪流用相同的key和隨機值在n個節點上請求鎖,在這一步裡,客戶端在每個master上請求鎖時,會有乙個和總的鎖釋放時間相比小的多的超時時間。比如如果鎖自動釋放時間是10秒鐘,那每個節點鎖請求的超時時間可能是5-50毫秒的範圍,這個可以防止乙個客戶端在某個宕掉的master節點上阻塞過長時間,如果乙個master節點不可用了,我們應該盡快嘗試下乙個master節點。

客戶端計算第二步中獲取鎖所花的時間,只有當客戶端在大多數master節點上成功獲取了鎖(在這裡是3個),而且總共消耗的時間不超過鎖釋放時間,這個鎖就認為是獲取成功了。

如果鎖獲取成功了,那現在鎖自動釋放時間就是最初的鎖釋放時間減去之前獲取鎖所消耗的時間。

如果鎖獲取失敗了,不管是因為獲取成功的鎖不超過一半(n/2+1)還是因為總消耗時間超過了鎖釋放時間,客戶端都會到每個master節點上釋放鎖,即便是那些他認為沒有獲取成功的鎖。

分析:

setnx:

redisson:

setnx:

redisson:

redisson:

使用redis分布式鎖一般選擇基於集群cluster方式的redisson實現。節點崩潰重啟問題和時間跳躍問題都屬於極端情況才會出現的問題,此時需要人工干預。

使用redisson框架編碼簡單且提供了豐富的鎖:公平、非公平鎖,聯合鎖、紅鎖、讀寫鎖等等

如果無預估計業務流程執行時間且要保證執行成功可以使用預設的lock,預設過期30s且會自動續期,但是會出現介面阻塞。如果上游無法忍受阻塞可以設定鎖等待時間,超時則返回失敗。

redis分布式鎖效率較高,可靠性較低。

原理:

zookeeper的分布式鎖是利用客戶端建立臨時唯一的有序節點(ephemeral_sequential)來保證同一時間只有乙個執行緒操作共享資源。在客戶端建立節點成功後會根據鎖的節點名(key)獲取對應的子節點,如果發現先自己建立的節點在在所有節點中是最小的,那麼認為鎖獲取成功。如果發現自己不是最小的節點,則找到比自己次小一節點去註冊監聽,掛起執行緒等待。釋放鎖只需要刪除相應的節點即可。

延伸:

zookeeper中有3中節點:

問題:

zookeeper在集群部署中,zookeeper節點數量一般是奇數(容錯成本和防止腦裂),且一定大等於3。

原理:

在client向follwer發出乙個寫的請求。

follwer把請求傳送給leader。

leader接收到以後開始發起投票並通知follwer進行投票。

follwer把投票結果傳送給leader,只要半數以上返回了ack資訊,就認為通過。

leader將結果彙總後如果需要寫入,則開始寫入同時把寫入操作通知給follwer,然後commit。

follwer把請求結果返回給client

問題:

分析:

效能較高、極端情況下可靠性較弱、預設情況下有鎖續期

效能較低、可靠性較強、不存在鎖釋放時間問題

單純比來看zk分布式鎖這塊可能更好些,尤其是對與併發不高的系統。但是最終我還是選擇了redis分布式鎖,為什麼呢,其實我覺得還有乙個因素不得不考慮,看你更加熟悉哪種技術棧,像redisson我之前就用過,我更加熟悉它是如何配置,如何使用。還有乙個就是雖然redis和zk都是依賴第三方元件,但是我覺得現在的專案來看redis是主流,基本上用到快取的專案都會用到redis,但是zk本身是乙個分布式協調工具,它大多在dubbo專案中大多用與在註冊中心,如果說我們的專案是spring cloud系呢,這時候註冊中心可能就是eureka或和consul了,這時候基於zk的分布式鎖就用不起來了。所以這個究竟用何種,還需要看具體專案來選擇。

最後附上redisson的demo鏈結

分布式 分布式鎖

本質是利用redis的setnx 方法的特性來加鎖,setnx 即key不存在則設定key,否則直接返回false,要求在分布式系統中使用同乙個redis服務,以下提供兩種解決方案 1 直接使用redistemplate 這其實並不能完全保證高併發下的安全問題,因為可能在鎖過期之後該執行緒尚未執行完...

C Redis分布式鎖的應用

我們在開發很多業務場景會使用到鎖,例如庫存控制,等。一般我們會使用記憶體鎖的方式來保證線性的執行。但現在大多站點都會使用分布式部署,那多台伺服器間的就必須使用同乙個目標來判斷鎖。分布式與單機情況下最大的不同在於其不是多執行緒而是多程序。分布式站點使用記憶體鎖方式如下圖 假設有3個使用者同時購買一件商...

分布式專題 分布式鎖

在傳統的單體應用架構中,遇到併發安全性問題時我們可以通過同步鎖synchronized,同步 塊,reentrantlock等方式都可以解決,但隨著業務的發展,單體應用架構不能滿足龐大的使用者請求量,於是分布式系統應用而生,在分布式系統中,由於每個系統都執行在不同的伺服器上,有著不同的jvm,所以j...