当前位置: 首页 > article >正文

Redission看门狗实现redis定期续期原理

文章目录

  • 前言
  • 前言问题解决思路
  • 如何开启redission看门狗
  • 看门狗核心代码
    • 定时续期实现
    • 解锁实现
  • Redission的bug相关记录
  • Redission依旧会产生需要考虑的问题

封面

前言

本篇博客是介绍redission看门狗实现redis定期续期原理,若文章中出现相关问题,请指出!

所有博客文件目录索引:博客目录索引(持续更新)

通常我们在使用redis上锁时,会进行业务处理,通常上锁我们会采用setnx或者lua脚本来保证并发场景下,只有一个线程能够抢到锁。

大多数的业务代码为:

setnx lockKey,超时时间
业务代码
delete lockKey

一旦超时时间过去了,redis对应的key就过期掉了。

场景说明:如果说key过期了,但是其中的业务在超时时间里没有完成,那么就可能会出现并发执行我们业务的问题,导致出现各种并发问题。


前言问题解决思路

案例demo来源:https://developer.aliyun.com/article/1627823

锁过期问题解决:

  1. 预估业务执行时间,提前预判业务执行时长。(一旦预估不准,就会导致并发问题)
  2. 模拟CAS乐观锁的方式,增加版本号。(侵入代码)
  3. watch dog自动延期机制。(起一个后台线程,默认超时时长为30s,每隔10s检查一次,一旦客户端还持有锁,就会不断延长key的过期时间)

解决思路2:在每次写操作时加入一个 token。 token 可以是一个递增的数字(lock service 可以做到),每次有 client 申请锁就递增一次。

在每次写操作时加入一个 token。 token 可以是一个递增的数字(lock service 可以做到),每次有 client 申请锁就递增一次。

示例如下:

首先客户端1获取到了token值43,一旦客户端1出现STW或者业务时间较长情况,出现了redis key过期的情况,锁被释放。
此时客户端2获取到了token值44,执行的很快,业务完成后又将token=44写入db。
此时客户端1这时候活过来了,执行完业务再进行插入的时候,由于当前其token=43是<44,表示该业务出现了锁过期并发情况,那么该写入操作就会被拒绝。

image-20241228122131094

缺点:入侵代码。

解决思路3:watch dog自动延期

客户端1进行加锁的同时,开启一个后台线程,这个线程每10s会检查下客户端1是否还持有锁,一旦还持有就会给它进行续期10s。

**本质:**在锁没有过期之前,不断的延长锁的有效期。

**优点:**不入侵业务代码,能够在真正业务执行完成之后锁释放。

**缺点:**无法避免STW情况,一旦出现STW无法执行续期代码逻辑。


如何开启redission看门狗

// 参数传递-1,开启看门狗,默认leaseTime超时时长为30s
boolean lock = rLock.tryLock(
                    lockWaitTime,
                    -1,
                    unit);

// 选择方法参数重不带leaseTime的,默认设置参数为-1 开启看门狗,默认leaseTime超时时长为30s
boolean lock = rLock.tryLock(
                    lockWaitTime,
                    unit);

一旦开启看门狗之后,会每隔leaseTime/3的时间进行续期leaseTime/3。


看门狗核心代码

下面只是指出最核心代码,并无细致逐行代码分析。

定时续期实现

业务代码:

RLock rLock = redissonClient.getLock(lockKey);
try {
    boolean lock = rLock.tryLock(
            lockWaitTime,
            lockLeaseTime,
            unit);

image-20241228202542859

tryLock上锁

image-20241228125413910

image-20241228125430879

该方法为定时续期:

image-20241228125520325

image-20241228125558878

image-20241228202702008

该段代码进行续期:主要判断就是 这个锁是否在redis中存在,如果存在就进行 pexpire 延期

image-20241228202802159

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {
    return this.evalWriteAsync(this.getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;", Collections.singletonList(this.getRawName()), this.internalLockLeaseTime, this.getLockName(threadId));
}

解锁实现

image-20241228203038402

image-20241228203143321

Redission的bug相关记录

当前项目中使用的redission版本为:

  <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson-spring-boot-starter</artifactId>
      <version>3.17.7</version>
  </dependency>

Redission 版本3.16.0:

  • 看门狗不生效问题 mr:https://github.com/redisson/redisson/issues/371。

Redisson 版本3.12.0:

  • 看门狗不生效问题 mr:https://github.com/redisson/redisson/issues/2515

Redission 版本3.10.3:

  • 看门狗死锁问题 mr:https://github.com/redisson/redisson/issues/1966

Redission依旧会产生需要考虑的问题

相关解决方案思路:

  • 如何解决Redisson中的WatchDog因JVM GC SWT存在的失效风险?:https://segmentfault.com/q/1010000044673539
  • redis分布式锁的思考:https://blog.csdn.net/weixin_43887958/article/details/118336001

下面列举可能遇到的场景:

  • JVM STW导致的锁过期问题,也就是JVM整个层面的stw情况,stw导致所有的线程都无法进行正常工作,如果时间过长,导致锁过期了,那么就需要在释放锁的时候去判断当前锁是否存在,不存在的话就抛出异常,若存在且是别的线程拿到的锁,也要抛出异常。
  • 另一种极端的情况:若是redis主从集群,那么有可能在异步复制时,主节点返回成功给线程1后,并未将数据同步到从节点就宕机了,之后从节点被升级为主节点,线程2也去获取锁A成功,线程1也获取成功。

**说明:**需要做一些补偿措施,一般的补偿措施就是将事务回滚。

**实现思路:**线程释放锁的时候应该判断当前锁是否存在,若不存在(1.key A过期,2.主节点未将key A同步到从节点就宕机了)就一定要回滚事务。在上述线程1和2都拿到锁的情况下,有没有问题呢?其实是没什么大问题的,因为线程A释放锁的时候会发现持有者不是自己,释放不掉,这个时候怎么办?只需要做一下补偿就行了,例如将增删改的数据还原即可。



http://www.kler.cn/a/465085.html

相关文章:

  • 电商项目-基于ElasticSearch实现商品搜索功能(一)
  • Mac M2基于MySQL 8.4.3搭建(伪)主从集群
  • 解决npm报错:sill idealTree buildDeps
  • 《GICv3_Software_Overview_Official_Release_B》学习笔记
  • 域上的多项式环,整除,相通,互质
  • Deepseek v3 的笔记
  • CDGA|浅析自动化对数据治理的深远影响
  • 基于MPPT算法的光伏并网发电系统simulink建模与仿真
  • S2-016-RCE(CVE-2013-2251)--vulhub
  • SSM-Spring-IOC/DI注解开发
  • git@github.com:username/repository.git 报错:no such file or directory
  • 代码随想录算法训练营第49期总结
  • 从低通滤波器到高通滤波器及小波函数的构造-附Matlab源程序
  • k8s基础(3)—Kubernetes-Deployment
  • 数据挖掘——模型的评价
  • 机器学习 学习知识点
  • 比ftp好用的大文件传输方案
  • 纵览!报表控件 Stimulsoft Reports、Dashboards 和 Forms 2025.1 新版本发布!
  • 复习打卡大数据篇——HIVE 01
  • Elasticsearch名词解释
  • 基于深度学习的视觉检测小项目(三) 通过设计一个简单的用户界面设计了解pyside的基本套路
  • C# 设计模式(结构型模式):适配器模式
  • Redis 入门与实战指南
  • 自动化测试之Pytest框架(万字详解)
  • 迈向云原生网络的初期
  • web期末作业网页设计实例代码 (建议收藏) HTML+CSS+JS (网页源码)