Redis分布式鎖實(shí)戰(zhàn)

1、什么是分布式鎖

在單機(jī)部署的情況下,要想保證特定業(yè)務(wù)在順序執(zhí)行,通過JDK提供的synchronized關(guān)鍵字、Semaphore、ReentrantLock,或者我們也可以基于AQS定制化鎖。單機(jī)部署的情況下,鎖是在多線程之間共享的,但是分布式部署的情況下,鎖是多進(jìn)程之間共享的。那么分布式鎖要保證鎖資源的唯一性,可以在多進(jìn)程之間共享。

2、分布式鎖特性

保證同一個(gè)方法在某一時(shí)刻只能在一臺(tái)機(jī)器里一個(gè)進(jìn)程中一個(gè)線程執(zhí)行;

要保證是可重入鎖(避免死鎖);

要保證獲取鎖和釋放鎖的高可用;

3、分布式鎖實(shí)現(xiàn)

鎖釋放(finally);

鎖超時(shí)設(shè)置;

鎖刷新(定時(shí)任務(wù),每2/3的鎖生命周期執(zhí)行);

如果鎖超時(shí)了,防止刪除其他線程的鎖(其他線程會(huì)拿到鎖),考慮 value值用線程id標(biāo)識(shí),當(dāng)前線程釋放鎖的時(shí)候要判斷是否為當(dāng)前線程的線程id;

可重入;

4、Redis分布式鎖

4.1、RedisLockRegistry

RedisLockRegistry是spring-integration-redis中提供redis分布式鎖實(shí)現(xiàn)類。主要是通過redis鎖+本地鎖雙重鎖的方式實(shí)現(xiàn)的一個(gè)比較好的鎖。

OBTAIN_LOCK_SCRIPT是一個(gè)上鎖的lua腳本。KEYS[1]代表當(dāng)前鎖的key值,ARGV[1]代表當(dāng)前的客戶端標(biāo)識(shí),ARGV[2]代表過期時(shí)間。

基本邏輯是:根據(jù)KEYS[1]從redis中拿到對(duì)應(yīng)的客戶端標(biāo)識(shí),如已存在的客戶端標(biāo)識(shí)和ARGV[1]相等,那么重置過期時(shí)間為ARGV[2];如果值不存在,設(shè)置KEYS[1]對(duì)應(yīng)的值為ARGV[1],并且過期時(shí)間是ARGV[2]。

獲取鎖的過程也很簡(jiǎn)單,首先通過本地鎖(localLock,對(duì)應(yīng)的是ReentrantLock實(shí)例)獲取鎖,然后通過RedisTemplate執(zhí)行OBTAIN_LOCK_SCRIPT腳本獲取redis鎖。

為什么要使用本地鎖呢,首先是為了鎖的可重入,其次是減輕redis服務(wù)壓力。

釋放鎖的過程也比較簡(jiǎn)單,第一步通過本地鎖判斷當(dāng)前線程是否持有鎖,第二步通過本地鎖判斷當(dāng)前線程持有鎖的計(jì)數(shù)。

如果當(dāng)前線程持有鎖的計(jì)數(shù) > 1,說(shuō)明本地鎖被當(dāng)前線程多次獲取,這時(shí)只釋放本地鎖(釋放之后當(dāng)前線程持有鎖的計(jì)數(shù)-1)。

如果當(dāng)前線程持有鎖的計(jì)數(shù) = 1,釋放本地鎖和redis鎖。

RedisLockRegistry使用如上所示。

首先定義RedisLockRegistry對(duì)應(yīng)的Bean,需要依賴redis的ConnectionFactory。

然后在服務(wù)層中注入RedisLockRegistry實(shí)例。

通過lock方法和unlock方法將業(yè)務(wù)邏輯包起來(lái),需要注意的是unlock方法要寫在finally代碼塊中。

4.2、Redisson

Redisson是架設(shè)在Redis基礎(chǔ)上的一個(gè)Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。

充分的利用了Redis鍵值數(shù)據(jù)庫(kù)提供的一系列優(yōu)勢(shì),基于Java實(shí)用工具包中常用接口,為使用者提供了一系列具有分布式特性的常用工具類。

使得原本作為協(xié)調(diào)單機(jī)多線程并發(fā)程序的工具包獲得了協(xié)調(diào)分布式多機(jī)多線程并發(fā)系統(tǒng)的能力,大大降低了設(shè)計(jì)和研發(fā)大規(guī)模分布式系統(tǒng)的難度。

同時(shí)結(jié)合各富特色的分布式服務(wù),更進(jìn)一步簡(jiǎn)化了分布式環(huán)境中程序相互之間的協(xié)作。

首先感受一下通過Redisson Api使用redis分布式鎖。

定義RedissonBuilder,通過redis集群地址構(gòu)建RedissonClient。

定義RedissonClient類型的Bean。

業(yè)務(wù)代碼里,通過RedissonClient獲取分布式鎖。

由于對(duì)Redisson分布式鎖實(shí)現(xiàn)原理了解的也不是很透徹,這里推薦一篇文章:Redisson 分布式鎖實(shí)現(xiàn)分析

4.3、Redisson和RedisLockRegistry對(duì)比

RedisLockRegistry通過本地鎖(ReentrantLock)和redis鎖,雙重鎖實(shí)現(xiàn),Redission通過Netty Future機(jī)制、Semaphore (jdk信號(hào)量)、redis鎖實(shí)現(xiàn)。

RedisLockRegistry和Redssion都是實(shí)現(xiàn)的可重入鎖。

RedisLockRegistry對(duì)鎖的刷新沒有處理,Redisson通過Netty的TimerTask、Timeout 工具完成鎖的定期刷新任務(wù)。

RedisLockRegistry僅僅是實(shí)現(xiàn)了分布式鎖,而Redisson處理分布式鎖,還提供了了隊(duì)列、集合、列表等豐富的API。

5、動(dòng)手實(shí)現(xiàn)分布式鎖

5.1、實(shí)現(xiàn)原理

本地鎖(ReentrantLock)+ redis鎖

5.2、獲取鎖lua腳本

5.3、鎖刷新lua腳本

5.4、鎖釋放lua腳本

5.5、本地鎖定義

每一個(gè)lock key對(duì)應(yīng)唯一的一個(gè)本地鎖

5.6、 線程標(biāo)識(shí)定義

分布式環(huán)境下,每一個(gè)線程對(duì)應(yīng)一個(gè)唯一標(biāo)識(shí)

5.7、鎖刷新定時(shí)任務(wù)定義

通過JDK ConcurrentTaskScheduler完成定時(shí)任務(wù)執(zhí)行,ScheduledFuture完成定時(shí)任務(wù)銷毀。其中taskId對(duì)應(yīng)線程標(biāo)識(shí)。

5.8、定義分布式鎖注解

5.9、分布式鎖切面

通過RedisLock注解實(shí)例lockInfo獲取到鎖key值、鎖過期時(shí)間信息。

5.10、獲取鎖過程

通過lockInfo.key()方法獲取到鎖key值,通過鎖key值拿到對(duì)應(yīng)的本地鎖(ReentrantLock)

本地鎖獲取鎖對(duì)象

進(jìn)入獲取redis鎖的循環(huán)

通過緩存服務(wù)組件執(zhí)行獲取鎖的lua腳本

如果獲取到redis鎖,判斷當(dāng)前線程是否第一次獲取到鎖并且開啟了鎖刷新,相應(yīng)的注冊(cè)鎖刷新定時(shí)任務(wù)

如果沒有獲取到redis鎖,休眠lockInfo.sleep()毫秒的時(shí)間,再次重試

5.11、釋放鎖過程

獲取到當(dāng)前鎖key值對(duì)應(yīng)的本地鎖

判斷當(dāng)前線程是否為本地鎖鎖的持有者

如果本地鎖的重入次數(shù)大于1,則只釋放本地鎖

如果本地鎖的重入次數(shù)等于1,釋放本地鎖和redis鎖

5.12、分布式鎖測(cè)試

定義測(cè)試類,測(cè)試方法注上@RedisLock注解,制定鎖的key值為 "redis-lock-test",測(cè)試方法內(nèi)隨機(jī)休眠。

開啟20個(gè)線程,同時(shí)調(diào)用測(cè)試方法。

多線程redis分布式鎖測(cè)試結(jié)果如下。

定義可重入測(cè)試類,方法內(nèi)獲取當(dāng)前代理對(duì)象,遞歸調(diào)用測(cè)試方法。

測(cè)試方法中,調(diào)用可重入測(cè)試類注有@RedisLock的測(cè)試方法。

分布式鎖可重入測(cè)試結(jié)果如下。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容