Redis之RedLock算法以及底層原理

自研redis分布式鎖存在的問題以及面試切入點
在這里插入圖片描述
lock加鎖關鍵邏輯
在這里插入圖片描述
在這里插入圖片描述
unlock解鎖的關鍵邏輯
在這里插入圖片描述
使用Redis的分布式鎖
在這里插入圖片描述
之前手寫的redis分布式鎖有什么缺點??
在這里插入圖片描述
在這里插入圖片描述
Redis之父的RedLock算法
Redis也提供了Redlock算法,用來實現基于多個實例的分布式鎖。
鎖變量由多個實例維護,即使有實例發生了故障,鎖變量仍然是存在的,客戶端還是可以完成鎖操作。
Redlock算法是實現高可靠分布式鎖的一種有效解決方案,可以在實際開發中使用
官網
Redis分布式鎖
在這里插入圖片描述

在這里插入圖片描述
RedLock的設計理念
該方案也是基于(set 加鎖、Lua 腳本解鎖)進行改良的,所以redis之父antirez 只描述了差異的地方,大致方案如下。

假設我們有N個Redis主節點,例如 N = 5這些節點是完全獨立的,我們不使用復制或任何其他隱式協調系統,

為了取到鎖客戶端執行以下操作:
在這里插入圖片描述
該方案為了解決數據不一致的問題,直接舍棄了異步復制只使用 master 節點,同時由于舍棄了 slave,為了保證可用性,引入了 N 個節點,官方建議是 5。
本次教學演示用3臺實例來做說明。客戶端只有在滿足下面的這兩個條件時,才能認為是加鎖成功。

條件1:客戶端從超過半數(大于等于N/2+1)的Redis實例上成功獲取到了鎖;
條件2:客戶端獲取鎖的總耗時沒有超過鎖的有效時間。

解決方案與容錯公式
在這里插入圖片描述

RedLock的落地實現Redisson

github地址

https://github.com/redisson/redisson
https://redisson.pro/docs/configuration/#cluster-mode

在這里插入圖片描述
pom文件

 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.19.1</version></dependency>

RedissonConfig配置類

package com.atguigu.redislock.config;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig
{@Beanpublic RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(lettuceConnectionFactory);//設置key序列化方式stringredisTemplate.setKeySerializer(new StringRedisSerializer());//設置value的序列化方式jsonredisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}@Beanpublic Redisson redisson(){Config config = new Config();config.useSingleServer().setAddress("redis://172.18.8.229:6379").setDatabase(0).setPassword("root");return (Redisson) Redisson.create(config);}
}

業務方法的改造

  @Autowiredprivate Redisson redisson;//V9版本public String saleV9(){String retMessage="";RLock redissonLock = redisson.getLock("redisLock");redissonLock.lock();try {//查詢庫存信息String result = stringRedisTemplate.opsForValue().get("inventory001");//判斷庫存是否足夠Integer inventory =result==null?0: Integer.valueOf(result);//扣減庫存if(inventory>0){stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventory));retMessage="成功賣出一個商品,庫存剩余:"+inventory;System.out.println(retMessage+"\t"+"服務端口號"+port);try {TimeUnit.SECONDS.sleep(120);} catch (InterruptedException e) {throw new RuntimeException(e);}}else{retMessage="商品賣完了";}}finally {redissonLock.unlock();}return retMessage+"\t"+"服務端口號"+port;}

Jemeter壓測
在這里插入圖片描述
在這里插入圖片描述
這樣直接刪除鎖是有bug的
在這里插入圖片描述
解決方案

if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread())
{
redissonLock.unlock();
} }

 @Autowiredprivate Redisson redisson;//V9版本public String saleV9(){String retMessage="";RLock redissonLock = redisson.getLock("redisLock");redissonLock.lock();try {//查詢庫存信息String result = stringRedisTemplate.opsForValue().get("inventory001");//判斷庫存是否足夠Integer inventory =result==null?0: Integer.valueOf(result);//扣減庫存if(inventory>0){stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventory));retMessage="成功賣出一個商品,庫存剩余:"+inventory;System.out.println(retMessage+"\t"+"服務端口號"+port);}else{retMessage="商品賣完了";}}finally {if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()){redissonLock.unlock();}        }return retMessage+"\t"+"服務端口號"+port;}

Redisson源碼解析

  • 加鎖
  • 可重入
  • 續命
  • 解鎖
  • 分析步驟
    Redis分布式鎖過期了,但是業務邏輯還沒處理完怎么辦?(還記得之前說過的緩存續命么)
    守護線程續命
    在這里插入圖片描述
    在獲取鎖成功后,給鎖加一個watch dog,watchdog會啟動一個定時任務,在鎖沒有被釋放且快要過期的時候會續期。
    在這里插入圖片描述

在這里插入圖片描述
源碼分析
在這里插入圖片描述
在這里插入圖片描述
通過redissson新建出來的鎖key,默認是30s

加鎖的核心代碼
在這里插入圖片描述
在這里插入圖片描述
Lua腳本加鎖
在這里插入圖片描述
在這里插入圖片描述

  • 通過 exists 判斷,如果鎖不存在,則設置值和過期時間,加鎖成功。
  • 通過 hexists 判斷,如果鎖已存在,并且鎖的是當前線程,則證明是重入鎖,加鎖成功。
  • 如果鎖已存在,但鎖的不是當前線程,則證明有其他線程持有鎖。返回當前鎖的過期時間(代表了鎖 key 的剩余生存時間),加鎖失敗。
    看門狗的鎖續期
    在這里插入圖片描述
    在這里插入圖片描述
    客戶端A加鎖成功,就會啟動一個watch dog看門狗,他是一個后臺線程,會每隔10秒檢查一下,如果客戶端A還持有鎖key,那么就會不斷的延長鎖key的生存時間,默認每次續命又從30秒新開始
    在這里插入圖片描述
    在這里插入圖片描述
    Lua腳本執行看門狗的鎖續期
    在這里插入圖片描述
    在這里插入圖片描述
    解鎖方法
    在這里插入圖片描述
    在這里插入圖片描述
    多機案例
    理論參考
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述
    實戰演示:
    docker啟動三個redis實例
    在這里插入圖片描述
server.port=9090
spring.application.name=redlockspring.swagger2.enabled=truespring.redis.database=0
spring.redis.password=
spring.redis.timeout=3000
spring.redis.mode=singlespring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10spring.redis.single.address1=172.18.8.229:6382
spring.redis.single.address2=172.18.8.229:6383
spring.redis.single.address3=172.18.8.229:6384

redis三個實例對應的配置類

package com.atguigu.redis.redlock.config;import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfiguration {@AutowiredRedisProperties redisProperties;@BeanRedissonClient redissonClient1() {Config config = new Config();String node = redisProperties.getSingle().getAddress1();node = node.startsWith("redis://") ? node : "redis://" + node;SingleServerConfig serverConfig = config.useSingleServer().setAddress(node).setTimeout(redisProperties.getPool().getConnTimeout()).setConnectionPoolSize(redisProperties.getPool().getSize()).setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());if (StringUtils.isNotBlank(redisProperties.getPassword())) {serverConfig.setPassword(redisProperties.getPassword());}return Redisson.create(config);}@BeanRedissonClient redissonClient2() {Config config = new Config();String node = redisProperties.getSingle().getAddress2();node = node.startsWith("redis://") ? node : "redis://" + node;SingleServerConfig serverConfig = config.useSingleServer().setAddress(node).setTimeout(redisProperties.getPool().getConnTimeout()).setConnectionPoolSize(redisProperties.getPool().getSize()).setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());if (StringUtils.isNotBlank(redisProperties.getPassword())) {serverConfig.setPassword(redisProperties.getPassword());}return Redisson.create(config);}@BeanRedissonClient redissonClient3() {Config config = new Config();String node = redisProperties.getSingle().getAddress3();node = node.startsWith("redis://") ? node : "redis://" + node;SingleServerConfig serverConfig = config.useSingleServer().setAddress(node).setTimeout(redisProperties.getPool().getConnTimeout()).setConnectionPoolSize(redisProperties.getPool().getSize()).setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());if (StringUtils.isNotBlank(redisProperties.getPassword())) {serverConfig.setPassword(redisProperties.getPassword());}return Redisson.create(config);}
}

controller的演示方法

@RestController
@Slf4j
public class RedLockController
{public static final String CACHE_KEY_REDLOCK = "ATGUIGU_REDLOCK";@Autowired RedissonClient redissonClient1;@Autowired RedissonClient redissonClient2;@Autowired RedissonClient redissonClient3;@GetMapping(value = "/multilock")public String getMultiLock(){String taskThreadID = Thread.currentThread().getId()+"";RLock lock1 = redissonClient1.getLock(CACHE_KEY_REDLOCK);RLock lock2 = redissonClient2.getLock(CACHE_KEY_REDLOCK);RLock lock3 = redissonClient3.getLock(CACHE_KEY_REDLOCK);RedissonMultiLock redLock = new RedissonMultiLock(lock1, lock2, lock3);redLock.lock();try{log.info("come in biz multilock:{}",taskThreadID);try { TimeUnit.SECONDS.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); }log.info("task is over multilock:{}",taskThreadID);}catch (Exception e){e.printStackTrace();log.error("multilock exception:{}",e.getCause()+"\t"+e.getMessage());}finally {redLock.unlock();log.info("釋放分布式鎖成功key:{}",CACHE_KEY_REDLOCK);}return "multilock task is over: "+taskThreadID;}
}

鎖續期成功
在這里插入圖片描述
宕機后仍然成功
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76891.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76891.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76891.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【控制學】控制學分類

【控制學】控制學分類 文章目錄 [TOC](文章目錄) 前言一、工程控制論1. 經典控制理論2. 現代控制理論 二、生物控制論三、經濟控制論總結 前言 控制學是物理、數學與工程的橋梁 提示&#xff1a;以下是本篇文章正文內容&#xff0c;下面案例可供參考 一、工程控制論 1. 經典…

Android 15 中 ApnPreferenceController 的 onStart 和 onStop 調用失效

背景 AOSP對APN入口(Access Point Name)實現中,overried了 onStart 和 onStop ,但實際執行中根本不會進入這兩個接口的邏輯。 Q:MobileNetworkSettings (APN入口Preference所在的界面Fragement承載,TAG是NetworkSettings)的生命周期和ApnPreference 有什么關系? Not…

React 在組件間共享狀態

在組件間共享狀態 有時候&#xff0c;你希望兩個組件的狀態始終同步更改。要實現這一點&#xff0c;可以將相關 state 從這兩個組件上移除&#xff0c;并把 state 放到它們的公共父級&#xff0c;再通過 props 將 state 傳遞給這兩個組件。這被稱為“狀態提升”&#xff0c;這…

階段性使用總結-通義靈碼

序言 前段時間用通義靈碼&#xff0c;參加了下數字中國閩江流域的比賽。https://www.dcic-china.com/competitions/10173 最后成績一般般&#xff0c;106名&#xff0c;大概有2000多人參加這題目&#xff0c;估計有一堆小號。 按照下面這個思路建模的&#xff0c;迭代了大概15…

游戲引擎學習第228天

對上次的內容進行回顧&#xff0c;并為今天的開發環節做鋪墊。 目前大部分功能我們已經完成了&#xff0c;唯一剩下的是一個我們知道存在但目前不會實際觸發的 bug。這個 bug 的本質是在某些線程仍然訪問一個已經被銷毀的游戲模式&#xff08;mode&#xff09;之后的狀態&…

游戲測試入門知識

高內聚指的是一個模塊或組件內部的功能應該緊密相關。這意味著模塊內的所有元素都應該致力于實現同一個目標或功能&#xff0c;并且該模塊應當盡可能獨立完成這一任務。 低耦合則是指不同模塊之間的依賴程度較低&#xff0c;即一個模塊的變化對其它模塊造成的影響盡可能小。理…

L1-2 種鉆石

題目 2019年10月29日&#xff0c;中央電視臺專題報道&#xff0c;中國科學院在培育鉆石領域&#xff0c;取得科技突破。科學家們用金剛石的籽晶片作為種子&#xff0c;利用甲烷氣體在能量作用下形成碳的等離子體&#xff0c;慢慢地沉積到鉆石種子上&#xff0c;一周“種”出了一…

基于開源技術生態的社群運營溫度化策略研究——以“開源鏈動2+1模式AI智能名片S2B2C商城小程序源碼”融合應用為例

摘要 在社交媒體與電商深度融合的背景下&#xff0c;社群運營的“溫度化”成為企業構建用戶忠誠度的核心命題。本文以康夏社群運營案例為切入點&#xff0c;結合“開源鏈動21模式AI智能名片S2B2C商城小程序源碼”技術架構&#xff0c;分析其通過開源技術實現情感聯結與商業價值…

編程技能:調試01,調試介紹

專欄導航 本節文章分別屬于《Win32 學習筆記》和《MFC 學習筆記》兩個專欄&#xff0c;故劃分為兩個專欄導航。讀者可以自行選擇前往哪個專欄。 &#xff08;一&#xff09;WIn32 專欄導航 上一篇&#xff1a;編程基礎&#xff1a;位運算07&#xff0c;右移 回到目錄 下一…

從零開始學A2A二 : A2A 協議的技術架構與實現

A2A 協議的技術架構與實現 學習目標 技術架構掌握 深入理解 A2A 協議的分層架構設計掌握各層次的功能和職責理解協議的工作原理和數據流 實現能力培養 能夠搭建基本的 A2A 服務端掌握客戶端開發方法實現智能體間的有效通信 架構設計理解 理解與 MCP 的本質區別掌握多智能體協…

UE5滾輪控制目標臂長度調整相機距離

UE5通過鼠標滾輪來控制攝像機目標臂長度 , 調整相機距離 看圖就行,不多說,照著連就完事了

python的strip()函數用法; 字符串切片操作

python的strip()函數用法 目錄 python的strip()函數用法代碼整體功能概述代碼詳細解釋1. `answer["output_text"]`2. `.strip()`3. `final_answer = ...`字符串切片操作:answer[start_index + len("Helpful Answer:"):].strip()整體功能概述代碼詳細解釋1…

云服務模式全知道:IaaS、PaaS、SaaS與DaaS深度解析

云服務模式詳解&#xff1a;IaaS、PaaS、SaaS與DaaS 在當今數字化快速發展的時代&#xff0c;云計算已經成為企業和開發者不可或缺的一部分。它提供了靈活的資源和服務&#xff0c;使得用戶可以根據自己的需求選擇最合適的解決方案。本文將詳細介紹四種主要的云服務模式&#…

AIDL 語言簡介

目錄 軟件包類型注釋導入AIDL 的后端AIDL 語言大致上基于 Java 語言。AIDL 文件不僅定義了接口本身,還會定義這個接口中用到的數據類型和常量。 軟件包 每個 AIDL 文件都以一個可選軟件包開頭,該軟件包與各個后端中的軟件包名稱相對應。軟件包聲明如下所示: package my.pac…

PINN:用深度學習PyTorch求解微分方程

神經網絡技術已在計算機視覺與自然語言處理等多個領域實現了突破性進展。然而在微分方程求解領域&#xff0c;傳統神經網絡因其依賴大規模標記數據集的特性而表現出明顯局限性。物理信息神經網絡(Physics-Informed Neural Networks, PINN)通過將物理定律直接整合到學習過程中&a…

程序化廣告行業(89/89):廣告創意審核的關鍵要點與實踐應用

程序化廣告行業&#xff08;89/89&#xff09;&#xff1a;廣告創意審核的關鍵要點與實踐應用 在程序化廣告這個充滿機遇與挑戰的領域&#xff0c;持續學習和知識共享是我們不斷進步的動力。一直以來&#xff0c;我都希望能和大家一同深入探索這個行業&#xff0c;今天讓我們聚…

【ES6新特性】Proxy進階實戰

&#x1f31f;ES6 Proxy終極指南&#xff1a;從攔截器到響應式框架實現&#x1f525; 一、&#x1f4a1; 為什么Proxy是革命性的&#xff1f;先看痛點場景 1.1 Object.defineProperty的局限 &#x1f62b; // Vue2響應式實現 let data { count: 0 }; Object.defineProperty(…

c++解決動態規劃

一、引言: 在我們學習了算法之后,我們一定遇到過貪心算法。而在貪心算法中就有著這樣一個經典的例子——湊錢。 Eg: 你有面額為10、5、1的紙幣,當你買菜時需要花費26元,請問需要最少的紙幣張數是多少。 當我們用貪心算法去解決這個問題的時候,我們…

Qwen 2.5 VL 多種推理方案

Qwen 2.5 VL 多種推理方案 flyfish 單圖推理 from modelscope import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor from qwen_vl_utils import process_vision_info import torchmodel_path "/media/model/Qwen/Qwen25-VL-7B-Instruct/"m…

機器視覺檢測Pin針歪斜應用

在現代電子制造業中&#xff0c;Pin針&#xff08;插針&#xff09;是連接器、芯片插座、PCB板等元器件的關鍵部件。如果Pin針歪斜&#xff0c;可能導致接觸不良、短路&#xff0c;甚至整機失效。傳統的人工檢測不僅效率低&#xff0c;還容易疲勞漏檢。 MasterAlign 機器視覺對…