【Redis】分布式锁之 Redission
一、基于setnx实现的分布式锁问题
重入问题:获得锁的线程应能再次进入相同锁的代码块,可重入锁能防止死锁。例如在HashTable中,方法用synchronized修饰,若在一个方法内调用另一个方法,不可重入会导致死锁。而synchronized和Lock锁都是可重入的。
不可重试:目前的分布式锁只能尝试一次,合理的情况是线程在获得锁失败后应能再次尝试。
超时释放:加锁时增加过期时间可防止死锁,但如果卡顿时间超长,虽采用了 lua 表达式防止删锁时误删别人的锁,但毕竟没有锁住,存在安全隐患。
主从一致性:若 Redis 提供主从集群,向集群写数据时,主机异步同步数据给从机,若同步前主机宕机,会出现死锁问题。

二、Redission 快速入门
引入依赖:根据项目需求引入 Redisson 相关依赖。
@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient(){// 配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.150.101:6379").setPassword("123321");// 创建RedissonClient对象return Redisson.create(config);}
}
配置 Redisson 客户端:进行 Redisson 客户端的配置。
@Resource
private RedissionClient redissonClient;@Test
void testRedisson() throws Exception{//获取锁(可重入),指定锁的名称RLock lock = redissonClient.getLock("anyLock");//尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位boolean isLock = lock.tryLock(1,10,TimeUnit.SECONDS);//判断获取锁成功if(isLock){try{System.out.println("执行业务"); }finally{//释放锁lock.unlock();}}
}
使用 Redission 的分布式锁:在VoucherOrderServiceImpl中注入RedissonClient,以使用 Redisson 的分布式锁功能。
@Resource
private RedissonClient redissonClient;@Override
public Result seckillVoucher(Long voucherId) {// 1.查询优惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);// 2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未开始return Result.fail("秒杀尚未开始!");}// 3.判断秒杀是否已经结束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {// 尚未开始return Result.fail("秒杀已经结束!");}// 4.判断库存是否充足if (voucher.getStock() < 1) {// 库存不足return Result.fail("库存不足!");}Long userId = UserHolder.getUser().getId();//创建锁对象 这个代码不用了,因为我们现在要使用分布式锁//SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);RLock lock = redissonClient.getLock("lock:order:" + userId);//获取锁对象boolean isLock = lock.tryLock();//加锁失败if (!isLock) {return Result.fail("不允许重复下单");}try {//获取代理对象(事务)IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);} finally {//释放锁lock.unlock();}}
三、Redission 可重入锁原理
在分布式锁中,Redission 采用 hash 结构存储锁。大 key 表示锁是否存在,小 key 表示当前锁被哪个线程持有。下面分析 lua 表达式的三个参数:
KEYS[1]:锁名称。
ARGV[1]:锁失效时间。
ARGV[2]:id + ":" + threadId,即锁的小 key。
执行过程如下:
redis.call('hset', KEYS[1], ARGV[2], 1),往 Redis 中写入数据,形成 hash 结构,如Lock{id + ":" + threadId : 1}。
"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hset', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"return redis.call('pttl', KEYS[1]);"
若当前锁存在,第一个条件不满足,接着判断redis.call('hexists', KEYS[1], ARGV[2]) == 1,通过大 key 和小 key 判断当前锁是否属于自己。若是自己的,则执行redis.call('hincrby', KEYS[1], ARGV[2], 1),将锁的 value 加 1,并执行redis.call('pexpire', KEYS[1], ARGV[1])设置过期时间。若以上两个条件都不满足,则抢锁失败,返回锁的失效时间。
查看源码会发现,会判断当前方法的返回值是否为null。若为null,对应前两个条件,退出抢锁逻辑;若返回值不是null,即走第三个分支,在源码处会进行while(true)的自旋抢锁。

四、Redission 锁重试和 WatchDog 机制
抢锁过程中,获得当前线程,通过tryAcquire进行抢锁,逻辑与之前相同:
先判断当前锁是否存在,若不存在,插入一把锁,返回null。
判断当前锁是否属于当前线程,若是,则返回null。
若返回值为null,代表当前线程已抢锁完毕或可重入完毕;若以上两个条件都不满足,则进入第三个条件,返回锁的失效时间。
long threadId = Thread.currentThread().getId();
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {return;
}
接下来根据lock方法的重载情况进行处理。若传入参数,leaseTime不为-1,则进行抢锁;
if (leaseTime != -1) {return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
}
若没有传入时间,也会进行抢锁,且抢锁时间是默认看门狗时间。
commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout()ttlRemainingFuture.onComplete((ttlRemaining, e)
这句话相当于对抢锁进行监听,抢锁完毕后会调用特定方法开启一个线程进行续约逻辑,即看门狗线程。
RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(waitTime,commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {if (e != null) {return;}// lock acquiredif (ttlRemaining == null) {scheduleExpirationRenewal(threadId);}
});
return ttlRemainingFuture;
续约逻辑是通过commandExecutor.getConnectionManager().newTimeout()方法实现的,该方法表示在一定时间后执行特定任务。以锁失效时间为 30s,10s 后触发任务进行续约,将锁续约成 30s,若操作成功,会递归调用自己,重新设置任务,实现不停续约。若线程出现宕机,则不会续约,等到时间后自然释放锁。
五、Redission 锁的 MutiLock 原理
为提高 Redis 的可用性,通常会搭建集群或主从。以主从为例,写命令在主机上,主机会将数据同步给从机,但在主机还未将数据写入从机时宕机,哨兵会选举一个 slave 变成 master,此时新的 master 中没有锁信息,锁就丢失了。

Redission 提出 MutiLock 锁来解决这个问题。使用 MutiLock 锁不使用主从,每个节点地位相同,加锁逻辑需写入到每个节点上,只有所有服务器都写入成功才是加锁成功。若某个节点挂了,只要有一个节点拿不到锁,都不算加锁成功,保证了加锁的可靠性。

当设置多个锁时,Redission 会将多个锁添加到一个集合中,用while循环不停尝试拿锁,但有一个总共的加锁时间,为需要加锁的个数乘以 1500ms。例如有 3 个锁,时间就是 4500ms,在这时间内所有锁加锁成功才算加锁成功,若有线程加锁失败,则会再次重试。

相关文章:
【Redis】分布式锁之 Redission
一、基于setnx实现的分布式锁问题 重入问题:获得锁的线程应能再次进入相同锁的代码块,可重入锁能防止死锁。例如在HashTable中,方法用synchronized修饰,若在一个方法内调用另一个方法,不可重入会导致死锁。而synchroni…...
对象序列化
Data AllArgsConstructor NoArgsConstructor public class Product implements Serializable {public Long productId;public String productName;public Double productPrice;public String productImg;public Integer productStatus;public String productCategory; }为什么要…...
什么是专利开放许可?
专利作为技术创新的重要载体,其有效转化与应用成为推动社会进步和经济发展的关键力量。那么,专利开放许可究竟是何方神圣?它如何打破传统专利许可的壁垒,促进创新资源的广泛共享? 专利开放许可的定义 专利开放许可&am…...
地表最强开源大模型!Llama 3.2,如何让你的手机变身私人智能助理
你有没有想过,为什么现在的手机越来越像小型电脑?无论是拍照、看视频,还是用各种APP,甚至是AI助手,手机的功能几乎无所不能。其实,这一切的背后有一个技术正在悄悄改变我们的生活,那就是Llama 3…...
Pandas中DataFrame表格型数据结构
目录 1、DataFrame是什么2、创建一个dataframe3、获取dataframe的行、列索引4、获取dataframe的值 1、DataFrame是什么 series是有一组数据与一组索引(行索引)组成的数据结构,而dataframe是由一组数据与一对索引(行索引和列索引&…...
C++的智能指针
很久之前,我们说到了new和delete关键字。 new在堆上分配内存,需要delete来删除内存、释放内存,因为它不会自动释放内存。 智能指针是实现过程自动化的一种方式,即当我们调用new时,我们不需要调用delete关键字。 在很…...
微信小程序showLoading ,showToast ,hideLoading连续调用出现showLoading 不关闭的情况记录
wx.showLoading({title: "操作中",mask: true,});api().then(() > {wx.showToast({title: "操作成功",icon: "none",});}).finally(() > {wx.hideLoading();}); 类似的代码偶尔会出现showLoading不关闭的现象, 这种情况下的解决方法就是 …...
OpenFeign使用详解
什么是OpenFeign? OpenFeign 是一个声明式的 HTTP 客户端,旨在简化微服务架构中不同服务之间的 HTTP 调用。它通过集成 Ribbon 实现了客户端负载均衡,并且能够与 Eureka、Consul 等服务发现组件无缝对接。使用 OpenFeign,开发者只…...
CSS clip-path 属性的使用
今天记录一个css属性clip-path,首先介绍下这个属性。 clip-path 是CSS中的一个神奇属性,它能够让你像魔术师一样,对网页元素施展“裁剪魔法”——只展示元素的一部分,隐藏其余部分。想象一下,不用依赖图片编辑软件&am…...
PHP 函数
PHP 函数 PHP(超文本预处理器)是一种广泛使用的开源服务器端脚本语言,特别适合于网页开发。在PHP中,函数是一段可重复使用的代码,用于执行特定任务。它们是PHP编程的核心组成部分,有助于模块化代码&#x…...
NCEloss与InfoNCEloss的区别
NCE Loss(Noise Contrastive Estimation Loss)和 InfoNCE Loss 是两种常用的损失函数,主要应用在对比学习和自监督学习任务中。它们的区别在于应用场景和具体实现细节。下面是对两者的详细比较: 1. NCE Loss(Noise Co…...
高通Android 12 push framework.jar和service.jar
1、Android framework.jar和service.jar替换注意事项 2、单编 adb push service.jar脚本 如下 adb root adb disable-verity adb remountadb push services.jar system/framework adb push services.jar.prof system/framework adb push oat/arm64/services.art /system/fram…...
HTTPS证书配置
NGINX、SSl配置 修改conf目录下NGINX中的crt和key文件 单点配置SSL 需要的文件和配置信息 证书和keytool.exe(使用jdk1.8的)工具要在同一个目录下 gxszy.qhxzhny.top.pfx(证书) keystorePass.txt(密码) 使用JDK自带的keyto…...
Image matting入门
概念 matting就是扣图,本质是预测前景与背景,将前景扣出来。主要应用于影视行业,如拍电影绿幕扣图。和图像分割的区别在于多一个模糊地带,非01分类,变成了预测alpha通道。前景F,背景B,图像I可以…...
基于安全风险预测的自动驾驶自适应巡航控制优化
摘要 :从周边车辆运动学状态参数和道路设施条件参数中提取场景特征指标和安全风险度量指标,采用极端梯度提升模型(XGboost )和长短时记忆模型( LSTM )进行安全风险预测,由此提出基于安全风险预测的自动驾驶自适应巡航控制(ACC )优化方法,并选取碰撞发生概率、速度平均…...
Docker Compose 搭建 Redis 哨兵集群模式搭建详解(1主2从+3哨兵)(包含主从复制的搭建) (保证一遍学会)
目录 哨兵的作用和工作原理 服务状态监控 选举新的 master 如何实现故障转移 搭建哨兵集群 哨兵的作用和工作原理 Redis 提供了哨兵 (Sentinel) 机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下 监控:Sentinel 会不断检查你的 master 和 slave 是否按…...
Oracle 单机和集群环境部署教程
目录 一、Oracle 单机环境部署1. 环境准备2. 安装 Oracle Database2.1 下载 Oracle Database2.2 创建 Oracle 用户和组2.3 配置内核参数和系统限制2.4 解压和安装2.5 配置监听程序2.6 创建数据库 3. 单机部署注意事项 二、Oracle 集群环境部署 (Oracle RAC)1. 环境准备2. 安装 …...
springboot 整合酷狗获取MV视频最高画质(使用自己账户)
在此声明,本内容仅供个人学习、研究或娱乐之用,严禁任何形式的商业用途。若您发现本内容被用于商业目的,请立即删除,及时与小编联系,我们将删除原代码。 请根据上一篇文章使用该代码:SpringBoot 整合酷狗获…...
数字孪生平台,助力制造设备迈入超感知与智控新时代!
痛点剖析 当前,制造业面临系统分散导致的数据孤岛问题,严重阻碍了有效监管与统计分析;同时,设备多样化且兼容性不足,增加了管理难度;台账记录方式混乱,工单审批流程繁琐且效率低下;…...
音视频入门基础:AAC专题(10)——FFmpeg源码中计算AAC裸流每个packet的pts、dts、pts_time、dts_time的实现
音视频入门基础:AAC专题系列文章: 音视频入门基础:AAC专题(1)——AAC官方文档下载 音视频入门基础:AAC专题(2)——使用FFmpeg命令生成AAC裸流文件 音视频入门基础:AAC…...
3步轻松掌握:163MusicLyrics歌词下载完全指南
3步轻松掌握:163MusicLyrics歌词下载完全指南 【免费下载链接】163MusicLyrics 云音乐歌词获取处理工具【网易云、QQ音乐】 项目地址: https://gitcode.com/GitHub_Trending/16/163MusicLyrics 还在为找不到高质量的LRC歌词而烦恼吗?163MusicLyri…...
Codesys ST语言PID调参避坑指南:从仿真到实战,手把手教你搞定温控/电机项目
Codesys ST语言PID调参避坑指南:从仿真到实战的工程化解决方案 在工业自动化领域,PID控制算法占据着核心地位。无论是恒温控制、电机调速还是压力调节,一个精心调校的PID控制器往往能决定整个系统的性能表现。然而,许多工程师在掌…...
西门子PLC通信必备:手把手教你用SCL编写Modbus RTU CRC校验功能块
西门子PLC通信实战:SCL实现Modbus RTU CRC校验的工程化解决方案 在工业自动化领域,可靠的数据通信如同设备的神经系统。当两台PLC需要通过RS485接口交换温度传感器读数时,Modbus RTU协议因其简洁高效成为首选。但许多工程师在调试阶段都会遇到…...
Windows平台QT BLE开发避坑指南:从环境搭建到稳定通信
1. Windows平台QT BLE开发环境搭建 在Windows平台上使用QT进行BLE开发,首先需要确保开发环境正确配置。我遇到过不少开发者因为环境问题卡在第一步,白白浪费好几天时间。这里分享几个关键点: 编译器选择是第一个坑。实测发现必须使用MSVC编译…...
构建本地化个人助理系统:事件驱动架构与模块化设计实践
1. 项目概述:一个高度可定制的个人助理系统最近在GitHub上看到一个挺有意思的项目,叫“Personal-Assistant”,作者是idk-man69。光看名字,你可能会觉得这又是一个类似Siri或Google Assistant的语音助手,但点进去仔细研…...
Source Han Serif CN:企业级开源字体终极实战指南
Source Han Serif CN:企业级开源字体终极实战指南 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 在当今数字化时代,企业面临字体选择的两难困境:商…...
AI智能体生态的包管理器:agenticmarket-cli 设计与实践
1. 项目概述:一个面向AI智能体生态的命令行工具如果你和我一样,长期在AI智能体(Agent)这个领域里折腾,那你肯定经历过这样的场景:为了测试一个最新的开源智能体框架,你需要先找到它的GitHub仓库…...
Unlock Music Electron:3步解锁你的加密音乐文件,重获音乐自由终极指南
Unlock Music Electron:3步解锁你的加密音乐文件,重获音乐自由终极指南 【免费下载链接】unlock-music-electron Unlock Music Project - Electron Edition 在Electron构建的桌面应用中解锁各种加密的音乐文件 项目地址: https://gitcode.com/gh_mirro…...
基于PIR传感器与LIFX智能灯泡的物联网运动感应照明系统实战
1. 项目概述与核心价值如果你对智能家居自动化感兴趣,并且想亲手打造一个既实用又有趣的照明项目,那么这个基于Adafruit FunHouse和LIFX智能灯泡的运动感应照明系统,绝对是一个绝佳的起点。它不仅仅是一个“开灯关灯”的简单触发器࿰…...
Mantic.sh:Bash脚本实现的终端命令自动化与效率提升工具
1. 项目概述:一个为开发者打造的终端效率工具如果你和我一样,每天有超过一半的工作时间是在终端(Terminal)里度过的,那你肯定对效率工具有着近乎偏执的追求。从cd到ls,从grep到awk,我们依赖这些…...
