Redis 分布式锁续期为什么会失效:租约、看门狗与释放边界
来源:17golang原创
时间:2026-08-27 06:49:19 419浏览 收藏
订单对账任务偶尔会出现两份结果同时写入,日志里却只看到两个 worker 都成功拿到过 Redis 锁。真正的问题通常不在第一次加锁,而在任务跑过租约之后:旧 worker 还在续期,锁却已经被新 worker 重新拿走,随后旧 worker 又把新 worker 的锁删掉。
- Redis 锁的 TTL 是租约,不是“进程活着就永远有效”的所有权证明。
- 续期必须同时检查锁值仍是当前持有者的随机令牌,不能单独执行
PEXPIRE。 - 释放也要做值比较后删除,避免过期后误删后来者的锁。
- 任务可能超过续期能力或要保护外部数据库时,还需要围栏令牌和业务侧版本校验。
先把 Redis 锁看成一张有期限的租约
最小加锁写法通常是:
SET lock:reconcile:20260827 worker-a-token NX PX 30000
NX 保证只有不存在时才创建,PX 30000 给这次占用设置 30 秒有效期,值里的随机令牌则用来标识“这次是谁拿到的锁”。Redis 官方文档也把随机值和过期时间放在同一个 SET 操作里,这是避免加锁与设过期时间之间出现空窗的关键。
这里的 30 秒只能说明 Redis 允许这把锁在这段时间内被当前持有者使用,不能证明 worker 一定在 30 秒内完成。GC 停顿、容器 CPU 被限流、下游接口卡住,都会让业务执行时间超过租约。

续期模式适合什么压力
当任务由多个可分段的小步骤组成,可以把初始 TTL 设得相对短,再在剩余时间接近阈值时续租。例如 30 秒租约在剩余 10 秒时发起一次续期,并要求连续几次失败后立刻停止后续写入。这样做的价值是:worker 崩溃时锁能较快自动释放,正常长任务则不必把初始 TTL 拉到几小时。
续期线程或定时器不能只看“本地任务还活着”。它必须向 Redis 确认 key 仍存在,并且 value 仍等于本次持有者的令牌。Redis 官方分布式锁说明给出的续期思路也是用 Lua 在服务端原子地检查值再延长 TTL:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('pexpire', KEYS[1], ARGV[2])
else
return 0
end
返回 1 只能说明这一次续期成功。业务代码还要把续期失败当成“失去锁”的信号,停止写库、停止提交消息,或把当前步骤标记为需要人工复核;不能继续跑完再凭经验猜测谁是主人。
看门狗不是永动机
很多客户端把自动续期叫作看门狗。它本质上只是一个后台续租循环,通常包含三个部分:持有者令牌、续租周期和失败收敛动作。它解决的是“任务正常运行但耗时超过初始 TTL”,解决不了网络隔离、Redis 故障、进程长时间暂停或客户端误判。
假设续租周期是 10 秒、TTL 是 30 秒,不能把 20 秒的网络抖动当作正常波动。连续失败后,旧 worker 可能在 Redis 里已经失去锁,但本地代码仍在修改订单。这里别急着调大 TTL,先定义失锁后的业务动作:是否可重试、已完成的子步骤如何幂等、外部系统是否支持撤销。
如果一次任务只能整体执行,续租也不能把它变成真正可抢占的事务。续租次数应有上限,或者由任务本身设置最长允许运行时间;否则一个失控 worker 会长期占住资源,活性反而变差。
释放锁时必须验证“还是我的吗”
错误的释放方式是直接删除:
DEL lock:reconcile:20260827
时间线可能是这样的:worker A 拿到锁后暂停,TTL 到期;worker B 拿到同名锁;A 恢复并执行 DEL。A 删除的已经不是自己的锁,B 的互斥保护就此失效。
正确的释放需要在 Redis 内完成比较和删除:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
脚本返回 1 表示这次删除确实匹配了当前令牌,返回 0 则说明锁已过期、被别人持有,或者 key 已经不存在。后两种情况都不要重试删除。

续期失效后,业务写入还安全吗
单实例 Redis 锁主要提供一个协作层面的互斥入口,并不替数据库或对象存储承担最终一致性责任。只要临界区里还有一个不能撤销的外部写操作,就要考虑“旧持有者晚到”的情况。
常见的补强方式是围栏令牌:每次成功获取锁时同时拿到一个递增序号,写入下游时带上这个序号;下游只接受大于已见序号的写入。这样即便旧 worker 在锁过期后恢复,较小的令牌也会被拒绝。围栏校验必须落在真正接受写入的那一侧,单独在 worker 内比较时间不能替代它。
如果下游没有围栏能力,至少把操作拆成可幂等步骤,记录任务版本和提交状态,并在每个不可逆动作前重新确认锁状态。这个办法不能消除所有竞态,但能把“重复写入”从静默覆盖变成可发现、可恢复的失败。
上线前按这张清单判断是否该用续期锁
- 加锁是否使用一次性
SET key token NX PX ttl,而不是分开的创建与过期命令? - 续期是否在 Redis 内原子比较 token 后再执行
PEXPIRE? - 续期返回 0、连接失败和本地暂停时,业务是否会停止新的外部写入?
- 释放是否使用同样的 token 比较后删除,且不会对失败结果盲目重试?
- 最长任务时长、续租次数和 Redis 不可用时的恢复路径是否明确?
- 下游写入是否需要围栏令牌、版本号或幂等键来挡住迟到的旧 worker?
常见问题
把 TTL 调得足够长,是不是就不用续期了?
只能降低正常任务超过 TTL 的概率,不能处理进程暂停、网络隔离和异常重试。TTL 越长,锁在持有者已经消失后释放得越慢;仍要按业务可接受的恢复时间来选。
续期用 PEXPIRE,为什么还要 Lua?
单独的 PEXPIRE 不会验证 value。key 在检查和续期之间可能已经被别人重新创建,Lua 可以把比较与续期放在同一个 Redis 原子操作里。
释放脚本返回 0 要不要再删一次?
不要。返回 0 正说明当前 key 不再属于这个令牌;再次删除可能误伤后来者。记录失锁事件并让任务按幂等或补偿路径收敛更安全。
Redlock 能消除旧 worker 晚到的问题吗?
不能把它当成绝对保证。Redis 官方文档明确建议对需要强一致的资源考虑围栏令牌,并认真评估时钟、网络分区和故障恢复条件。锁算法只能覆盖锁服务本身的边界,不能自动约束所有下游写入。
把锁的边界写进验收结果
一把可维护的 Redis 分布式锁,最重要的不是“看门狗开了”,而是每个状态都有明确动作:拿不到锁就退出,续期失败就停止新的不可逆写入,释放前验证令牌,外部资源用版本或围栏令牌挡住迟到者。先用短 TTL、可观测的续期日志和故障注入测出真实边界,再决定是否需要更复杂的多节点方案。
-
366 收藏
-
117 收藏
-
426 收藏
-
171 收藏
-
113 收藏
-
403 收藏
-
数据库 · Redis | 4小时前 | Redis · Redis性能 · 延迟排查 · 运维监控 · Redis LATENCY HISTOGRAM Redis 延迟直方图 命令耗时分布 latency-tracking339 收藏
-
数据库 · Redis | 5小时前 | Redis · Redis Cluster · 故障排查 · Pub/Sub · Redis Cluster Redis PUBSUB SHARDCHANNELS Redis 分片订阅 SHARDNUMSUB228 收藏
-
数据库 · Redis | 7小时前 | Redis · 内存管理 · 性能排查 · 碎片率 · 运维验证 · redis 内存回收 INFO memory allocator_frag_ratio 内存碎片率261 收藏
-
232 收藏
-
164 收藏
-
331 收藏
-
489 收藏
-
101 收藏
-
119 收藏
-
460 收藏
-
数据库 · Redis | 19小时前 | Redis · 内存优化 · 数据库运维 · 性能排查 · 命令诊断 · redis 内存优化 OBJECT ENCODING listpack Redis运维233 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习