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

文章目录

前言

本篇博客是介绍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,表示该业务出现了锁过期并发情况,那么该写入操作就会被拒绝。

缺点:入侵代码。

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

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

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

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

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


如何开启redission看门狗

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

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

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


看门狗核心代码

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

定时续期实现

业务代码:

java 复制代码
RLock rLock = redissonClient.getLock(lockKey);
try {
    boolean lock = rLock.tryLock(
            lockWaitTime,
            lockLeaseTime,
            unit);

tryLock上锁

该方法为定时续期:

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

java 复制代码
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));
}

解锁实现

Redission的bug相关记录

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

xml 复制代码
  <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson-spring-boot-starter</artifactId>
      <version>3.17.7</version>
  </dependency>

Redission 版本3.16.0:

Redisson 版本3.12.0:

Redission 版本3.10.3:


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

相关解决方案思路:

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

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

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

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


相关推荐
程序员张32 小时前
Maven编译和打包插件
java·spring boot·maven
Hello.Reader3 小时前
Redis 延迟监控深度指南
数据库·redis·缓存
ybq195133454313 小时前
Redis-主从复制-分布式系统
java·数据库·redis
马里奥Marioぅ3 小时前
Redis主从切换踩坑记:当Redisson遇上分布式锁的“死亡连接“
redis·分布式锁·redisson·故障转移
weixin_472339463 小时前
高效处理大体积Excel文件的Java技术方案解析
java·开发语言·excel
小毛驴8504 小时前
Linux 后台启动java jar 程序 nohup java -jar
java·linux·jar
DKPT4 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
好奇的菜鸟6 小时前
如何在IntelliJ IDEA中设置数据库连接全局共享
java·数据库·intellij-idea
tan180°6 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
满昕欢喜6 小时前
SQL Server从入门到项目实践(超值版)读书笔记 20
数据库·sql·sqlserver