基於Redis實現分布式鎖

2022-05-05 02:45:07 字數 3619 閱讀 4469

我們知道分布式鎖的特性是排他、避免死鎖、高可用。分布式鎖的實現可以通過資料庫的樂觀鎖(通過版本號)或者悲觀鎖(通過for update)、redis的setnx()命令、zookeeper(在某個持久節點新增臨時有序節點,判斷當前節點是否是序列中最小的節點,如果不是則監聽比當前節點還要小的節點。如果是,獲取鎖成功。當被監聽的節點釋放了鎖(也就是被刪除),會通知當前節點。然後當前節點再嘗試獲取鎖,如此反覆)

本篇文章,主要講如何用redis的形式實現分布式鎖。後續文章會講解熱點key讀取,快取穿透和快取雪崩的場景和解決方案、快取更新策略等等知識點,理論知識點較多。

spring:

redis:

port: 6379

database: 0

host: 127.0.0.1

password:

jedis:

pool:

max-active: 8

max-wait: -1ms

max-idle: 8

min-idle: 0

timeout: 5000ms

public class jedisconnectionutil 

public static jedis getjedis()

}

下面進入正文。因為分布式系統之間是不同程序的,單機版的鎖無法滿足要求。所以我們可以借助中介軟體redis的setnx()命令實現分布式鎖。setnx()命令只會對不存在的key設值,返回1代表獲取鎖成功。對存在的key設值,會返回0代表獲取鎖失敗。這裡的value是system.currenttimemillis() (獲取鎖的時間)+鎖持有的時間。我這裡設定鎖持有的時間是200ms,實際業務執行的時間遠比這200ms要多的多,持有鎖的客戶端應該檢查鎖是否過期,保證鎖在釋放之前不會過期。因為客戶端故障的情況可能是很複雜的。比如現在有a,b倆個客戶端。a客戶端獲取了鎖,執行業務中做了騷操作導致阻塞了很久,時間應該遠遠超過200ms,當a客戶端從阻塞狀態下恢復繼續執行業務**時,a客戶端持有的鎖由於過期已經被其他客戶端占有。這時候a客戶端執行釋放鎖的操作,那麼有可能釋放掉其他客戶端的鎖。

我這裡設定的客戶端等待鎖的時間是200ms。這裡通過輪詢的方式去讓客戶端獲取鎖。如果客戶端在200ms之內沒有鎖的話,直接返回false。實際場景要設定合適的客戶端等待鎖的時間,避免消耗cpu資源。

/**

* 獲取鎖

* @param lockname 鎖的名字

* @param acquiretimeout 或得所的超時時間

* @param locktimeout 所本身的超時時間

* @return

*/public string acquirelock(string lockname, long acquiretimeout, long locktimeout)

if (jedis.ttl(lockkey) == -1)

try catch (interruptedexception e)

}}finally

return null;

}

所以,我們要加上鎖過期,然後獲取鎖的策略。通過realkey獲取當前的currentvalue。currentvalue也就是獲取鎖的時間 + 鎖持有的時間。 如果currentvalue不等於null 且 currentvalue 小於當前時間,說明鎖已經過期。這時候如果突然來了c,d兩個客戶端獲取鎖的請求,不就讓c,d兩個客戶端都獲取鎖了嗎。如果防止這種現象發生,我們採用getset()命令來解決。getset(key,value)的命令會返回key對應的value,然後再把key原來的值更新為value。也就是說getset()返回的是已過期的時間戳。如果這個已過期的時間戳等於currentvalue,說明獲取鎖成功。

/**

* 釋放鎖

* @param lockname 鎖的名字

* @param identifler 鎖標識

* @return

*/public boolean releaselock(string lockname, string identifler)

isrelease = true;

}// todo 異常

jedis.unwatch();

break;

}}finally

return isrelease;

}

編寫測試類用多執行緒模擬高併發

public class unittest extends thread  catch (interruptedexception e) 

break;}}

}public static void main(string args) }}

**執行結果

tname:0-> 或得鎖a86a2867-a5f9-4b81-8be6-105d50b8a1ab

updateorder釋放鎖a86a2867-a5f9-4b81-8be6-105d50b8a1ab

tname:1-> 或得鎖bcdeb19c-a47c-4a12-86df-8e5d90b2ecb0

updateorder釋放鎖bcdeb19c-a47c-4a12-86df-8e5d90b2ecb0

tname:6-> 或得鎖147d9fc8-5e15-4e86-8da6-e029a3e2d92f

updateorder釋放鎖147d9fc8-5e15-4e86-8da6-e029a3e2d92f

tname:2-> 或得鎖93deb41d-2439-45e7-beb1-2e4ce672e8ca

updateorder釋放鎖93deb41d-2439-45e7-beb1-2e4ce672e8ca

tname:4-> 或得鎖094f921d-fe9b-46ba-873b-aee2ce974f16

updateorder釋放鎖094f921d-fe9b-46ba-873b-aee2ce974f16

tname:5-> 或得鎖216e0799-6d22-4ae4-bb83-9efe1e5f80c9

updateorder釋放鎖216e0799-6d22-4ae4-bb83-9efe1e5f80c9

tname:9-> 或得鎖678e2099-651c-4e23-a648-9fb588ecb42b

updateorder釋放鎖678e2099-651c-4e23-a648-9fb588ecb42b

tname:7-> 或得鎖f35cdbad-6fde-4f1e-a4c1-321805a39374

updateorder釋放鎖f35cdbad-6fde-4f1e-a4c1-321805a39374

tname:3-> 或得鎖0913a4a0-805a-48e2-ac5a-c0762b8c4072

updateorder釋放鎖0913a4a0-805a-48e2-ac5a-c0762b8c4072

tname:8-> 或得鎖a3964a2a-6b9c-4ca3-9ea5-53de854c23ce

updateorder釋放鎖a3964a2a-6b9c-4ca3-9ea5-53de854c23ce

本文文字部分參考文章:

基於Redis實現分布式鎖

分布式鎖的基本功能 1.同一時刻只能存在乙個鎖 2.需要解決意外死鎖問題,也就是鎖能超時自動釋放 3.支援主動釋放鎖 分布式鎖解決什麼問題 多程序併發執行任務時,需要保證任務的有序性或者唯一性 準備 redis版本 2.6 redis是主從 sentinel模式 為了高可用 原理 redis2.6之...

基於Redis實現分布式鎖

之前專案中使用redis鎖實現秒殺等一些併發業務,在這裡整理一下基於redis實現分布式鎖的簡單入門例項,記錄一下,便於以後檢視 學習。springboot整合redisson分布式鎖 1 簡介 在分布式系統中存在併發場景,為了解決這一問題,基於redis鎖一定程度可以解決這一問題,但是也有缺點,如...

基於redis實現分布式鎖

實現方式 具備條件 確保鎖可用,必須要滿足一下幾個條件 1 互斥性,任意時刻只有乙個使用者能持有鎖 2 不會產生死鎖,假設某個使用者在持有鎖的期間由於服務崩潰或者其他原因沒有主動釋放鎖,也能保證後續其他使用者可以加鎖 3 加鎖和解鎖必須是同一使用者,b使用者無法解除a使用者加的鎖 實現 1 引入je...