缓存-分布式锁-分布式锁原理与使用
怎么來實現這個分布式鎖呢?
方案一:
這種設計方案,會出現一個問題:當線程獲取到鎖,然后執行完業務邏輯,準備去刪除鎖的時候,突然服務器宕機了,會導致這個鎖一直存在,得不到釋放,會造成死鎖的情況。
解決方案就是:設置一個過期時間,即使服務器宕機不能手動釋放,也可以過期自動釋放
方案二:
解決了方案一的問題,不過,還會有問題,假如當我們獲取到鎖之后將要去設置過期時間的時候,這時候服務器宕機了,也會造成死鎖情況。
解決方案:保證獲取鎖和設置過期時間是原子性的,setnx ex命令可以保證原子性
方案三:
這種方案解決了設置鎖的原子性,但是在刪除鎖的時候,是應該直接刪除的嗎?當我們的業務執行時間很長的時候,這時候假定鎖已經過期了,別的線程獲得了鎖,先前線程執行完業務之后,去刪除鎖,就會去刪除別人的鎖
解決方案:在設置鎖的時候指定自己的UUID,執行完業務后,獲取鎖檢查是否是自己之前設置的,如果是自己設置的,就刪除,否則就跳過,在刪除鎖的時候也要保證原子性,為什么呢?假如我們獲取到這個鎖的確是我們自己之前設置的,但是在獲取值到刪除鎖中間還是有一段時間,假如這段時間,鎖失效了,別人獲取到了鎖,這時候我們還是會認為鎖是自己的,會導致誤刪。
方案四:
手動如何實現分布式鎖
實現分布式鎖的前提一定要保證在獲取到鎖+過期時間、獲取鎖+刪除鎖這兩步操作都是原子操作。
上圖就是我解決方案的流程圖。接下來就看看我代碼是怎么實現的吧。
/*** 從數據庫查詢并封裝數據::分布式鎖* @return*/ public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() {//1、占分布式鎖。去redis占坑 設置過期時間必須和加鎖是同步的,保證原子性(避免死鎖)String uuid = UUID.randomUUID().toString();Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);if (lock) {System.out.println("獲取分布式鎖成功...");Map<String, List<Catelog2Vo>> dataFromDb = null;try {//加鎖成功...執行業務(只允許獲取到分布式鎖的線程去數據庫中查)dataFromDb = getDataFromDb();} finally {// Lua腳本,在腳本中有兩步操作:一、獲取當前這個分布式鎖,判斷這個分布式鎖是不是我的,二、如果是我的就刪除,并返回1,否則返回0String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";//刪除鎖stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);}//先去redis查詢下保證當前的鎖是自己的//獲取值對比,對比成功刪除=原子性 lua腳本解鎖// String lockValue = stringRedisTemplate.opsForValue().get("lock");// if (uuid.equals(lockValue)) {// //刪除我自己的鎖// stringRedisTemplate.delete("lock");// }return dataFromDb;} else {System.out.println("獲取分布式鎖失敗...等待重試...");//加鎖失敗...重試機制//休眠一百毫秒try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}return getCatalogJsonFromDbWithRedisLock(); //自旋的方式} }?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的缓存-分布式锁-分布式锁原理与使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SET key value [EX se
- 下一篇: 缓存-分布式锁-Redisson简介整合