为什么Redisson分布式锁续期必须依赖Redis与Lua?

风明君_5127

风明君_5127

2026-10-21

824人浏览

原创

续期不能用普通expire命令,因其无法校验锁归属,b线程可能误续a线程的锁,破坏互斥性;必须用lua脚本先检查uuid:threadid再pexpire,确保仅持有者可续期。

为什么redisson分布式锁续期必须依赖redis与lua?

续期为什么不能用普通EXPIRE命令

因为普通 EXPIRE 命令无法校验锁归属。如果 A 线程持有锁,B 线程在续期时直接对 key 执行 EXPIRE,就会覆盖掉 A 的锁并延长过期时间——这等于把别人的锁“续”成了自己的,彻底破坏互斥性。

真实场景中,这种误续往往发生在锁已释放、但续期任务尚未取消的窗口期(比如 JVM GC 暂停导致 WatchDog 未及时感知线程退出)。

  • Redis 单线程执行 Lua 脚本,天然隔离并发修改
  • 续期脚本必须先 hexists 检查 UUID:threadId 是否仍在 hash 中
  • 只有匹配才执行 pexpire,否则直接返回 0,WatchDog 收到后会主动取消后续调度

WatchDog 的定时调度为什么必须基于 Netty HashedWheelTimer

不是不能用 ScheduledThreadPoolExecutor,而是它做不到「低开销 + 高精度 + 自动漂移补偿」。

看门狗默认每 lockWatchdogTimeout / 3(即 10 秒)触发一次,但业务线程可能卡在 GC、Full GC 或远程调用里。HashedWheelTimer 的时间轮结构能容忍短暂延迟,且不创建额外线程——所有续期任务共用一个 I/O 线程,避免线程爆炸。

redis-tools
redis-tools

按类别查找Redis命令、检测服务器连接、监控键数量和内存占用,支持离线速查表。

下载
  • 若用普通定时器,在高并发锁场景下,每个锁都起一个 ScheduledFuture,极易耗尽线程资源
  • HashedWheelTimer 的 tickDuration 默认 10ms,误差可控;而 JDK 定时器在负载高时 drift 可达数百毫秒
  • 续期失败时,Redisson 会记录日志 Can't update lock expiration,但不会抛异常——这点容易被监控忽略

Lua 脚本里为什么要用 HINCRBY 而不是 SET

因为 Redisson 锁是可重入的,同一个线程多次 lock(),内部计数要累加;对应地,每次 unlock() 减一,归零才真正删 key。

如果续期时只用 SET 覆盖 value,就丢失了重入计数,导致 unlock() 时误判为「非持有者」而拒绝释放——业务上表现为:锁明明没释放,却再也 unlock 不掉。

  • HINCRBY 对应的 field 是 UUID:threadId,value 是重入次数(整数)
  • 续期脚本里 hincrby KEYS[1] ARGV[2] 0 是安全的读取计数操作,不改变值但确认存在
  • 直接 SET 会抹掉整个 hash 结构,让后续解锁脚本里的 hexists 判定失败

主从架构下续期失败的真实表现

当 Redis 集群发生主从切换,原 master 上的锁 key 还没同步到新 master,WatchDog 发起的续期请求打到旧 master(已降级为 slave),而 slave 默认拒绝写请求,返回 READONLY You can't write against a read only slave。

此时 WatchDog 不会重试,而是静默取消该锁的续期任务——锁将在剩余 TTL 后自动过期,但业务线程仍以为自己持有锁,从而引发数据错乱。

  • 这个错误不会抛出异常,也不会打印 ERROR 日志,只有 WARN 级别:Unable to send unlock message 或 Failed to renew lock
  • 必须开启 Redisson 的 setRetryAttempts(0) 并配合监控 redisson_lock_expired 指标才能捕获
  • 跨机房部署时,网络分区比主从切换更隐蔽,续期请求超时后同样静默终止
实际工程中最容易被忽略的,是 WatchDog 与业务线程生命周期的绑定关系:它不是全局后台线程,而是依附于加锁线程的栈帧存活。一旦线程结束(比如被 Thread.interrupt() 或容器回收),续期任务立即失效——这点在异步线程池或 Spring @Async 场景下尤其危险。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

redis

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2053

5

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.07

3134

5

常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.02

4549

19

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.14

4095

11

mongodb和redis哪个读取速度快
mongodb和redis哪个读取速度快

redis 的读取速度比 mongodb 更快。原因包括:1. redis 使用简单的键值存储,而 mongodb 存储 json 格式的数据,需要解析和反序列化。2. redis 使用哈希表快速查找数据,而 mongodb 使用 b-tree 索引。因此,redis 在需要高性能读取操作的应用程序中是一个更好的选择。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.02

7612

6

redis怎么做缓存服务器
redis怎么做缓存服务器

redis 作为缓存服务器的答案:redis 是一款开源、高性能、分布式的键值存储,可作为缓存服务器使用。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

643

6

redis怎么解决数据一致性
redis怎么解决数据一致性

redis 提供了两种一致性模型,以维护副本数据一致性:强一致性 (sync) 确保写操作仅在复制到所有从节点后才完成;最终一致性 (async) 则在主节点上写操作后认为已完成,牺牲一致性换取性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

756

6

mysql和redis怎么保证双写一致性
mysql和redis怎么保证双写一致性

确保 mysql 和 redis 双写一致性的技术包括:1、事务性更新:同时更新 mysql 和 redis,保证一致性;2、主从复制:mysql 主服务器更改同步到 redis 从服务器;3、基于事件的更新:mysql 记录更改并发送到 redis等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

7542

6

redis缓存一般存些什么数据
redis缓存一般存些什么数据

redis缓存中存储的数据类型包括:字符串、哈希、列表、集合、有序集合、位图、地理空间数据和hyperloglog。这些数据类型适用于存储各种数据,从简单信息到复杂对象和地理位置。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

1160

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
phpEnv手册
phpEnv手册

共0课时 | 0人学习