單機redis分布式鎖實現(xiàn)原理解析
最近我們有個服務(wù)經(jīng)常出現(xiàn)存儲的數(shù)據(jù)出現(xiàn)重復(fù),首先上一個系統(tǒng)流程圖:
用戶通過http請求可以通知任務(wù)中心結(jié)束掉自己發(fā)送的任務(wù),這時候任務(wù)中心會通過MQ通知結(jié)束服務(wù)去結(jié)束任務(wù)保存數(shù)據(jù),由于任務(wù)結(jié)束數(shù)據(jù)計算保存有一定延時,所以存在用戶短時間內(nèi)多次結(jié)束同一個任務(wù),這時候就會導(dǎo)致我們結(jié)束服務(wù)對同一個任務(wù)保存多次數(shù)據(jù)。恰好我們也是用了redis,所以對于這個問題我當時想到使用分布式鎖來解決,那么如何用redis實現(xiàn)分布式鎖呢?
首先要明確一個分布式鎖應(yīng)具備的原則:
互斥性。在任意時刻,只有一個客戶端能持有鎖;不會發(fā)生死鎖。即使一個客戶端持有鎖的期間崩潰而沒有主動釋放鎖,也需要保證后續(xù)其他客戶端能夠加鎖成功;加鎖和解鎖必須是同一個客戶端;有高可用的獲取鎖和釋放鎖功能。
由于我們只使用了單機的redis,所以本文的實現(xiàn)不具備第四點原則。
我們這個鎖的實現(xiàn)就包括兩點:加鎖、解鎖。首先看加鎖。先上代碼:
public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) throws Exception{ Jedis jedis = null; try { jedis = getJedisClient(); String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; } finally { returnResource(jedis); } }
我們的加鎖就是設(shè)置一個鍵值對,并且滿足以下條件:
確保只有當鍵不存在時才設(shè)置有效;設(shè)置的值必須是當前客戶端生成的uuid;鍵必須要有過期時間。
這三點條件就可以滿足上述的原則1、原則2。
接下來看下解鎖,代碼如下:
public boolean releaseDistributedLock(String lockKey, String requestId) throws Exception{ Jedis jedis = null; try { jedis = getJedisClient(); String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false; }finally { returnResource(jedis); } }
解鎖是通過一段lua腳本實現(xiàn),邏輯如下:
1、獲取鎖鍵值看是否與當初設(shè)置的值一致;
2、如果一致則刪除鍵。
由于解鎖過程分為兩步,為了確保原子性所以通過讓redis執(zhí)行l(wèi)ua腳本來實現(xiàn),校驗鍵值可以確保加鎖解鎖都是同一個客戶端。
這樣一個簡易的分布式鎖就實現(xiàn)完畢了,當然在本文開頭就說了,這個實現(xiàn)只能滿足單機redis的情況,對于redis集群其實是不嚴謹?shù)?,對于redis集群有一個redlock方案,我也在研究中,后面也會總結(jié)一下。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ExecutorService Callable Future多線程返回結(jié)果原理解析
這篇文章主要為大家介紹了ExecutorService Callable Future多線程返回結(jié)果,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09解決mybatis 執(zhí)行mapper的方法時報空指針問題
這篇文章主要介紹了解決mybatis 執(zhí)行mapper的方法時報空指針問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07springboot中RabbitMQ死信隊列的實現(xiàn)示例
死信隊列是一種特殊的消息隊列,用來存儲無法被正常消費的消息,常被用來實現(xiàn)延遲處理,異常消息處理等,本文主要介紹了springboot中RabbitMQ死信隊列的實現(xiàn)示例,感興趣的可以了解一下2024-01-01Spring中網(wǎng)絡(luò)請求客戶端WebClient的使用詳解
作為替代,Spring 官方已在 Spring 5 中引入了 WebClient 作為非阻塞式 Reactive HTTP 客戶端,本文將通過樣例演示如何使用 WebClient,希望對大家有所幫助2024-04-04springboot使用AOP+反射實現(xiàn)Excel數(shù)據(jù)的讀取
本文主要介紹了springboot使用AOP+反射實現(xiàn)Excel數(shù)據(jù)的讀取,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01