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.mzph.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 机器视觉对…