当前位置:首页 > 文章列表 > 数据库 > Redis > Redis 分布式锁续期为什么会失效:租约、看门狗与释放边界

Redis 分布式锁续期为什么会失效:租约、看门狗与释放边界

来源:17golang原创 2026-08-27 06:49:19 0浏览 收藏

订单对账任务偶尔会出现两份结果同时写入,日志里却只看到两个 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 被限流、下游接口卡住,都会让业务执行时间超过租约。

Redis 分布式锁租约从获取、续期到过期的时间窗口示意

续期模式适合什么压力

当任务由多个可分段的小步骤组成,可以把初始 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 分布式锁在令牌匹配、令牌不匹配和锁已过期之间的释放边界

续期失效后,业务写入还安全吗

单实例 Redis 锁主要提供一个协作层面的互斥入口,并不替数据库或对象存储承担最终一致性责任。只要临界区里还有一个不能撤销的外部写操作,就要考虑“旧持有者晚到”的情况。

常见的补强方式是围栏令牌:每次成功获取锁时同时拿到一个递增序号,写入下游时带上这个序号;下游只接受大于已见序号的写入。这样即便旧 worker 在锁过期后恢复,较小的令牌也会被拒绝。围栏校验必须落在真正接受写入的那一侧,单独在 worker 内比较时间不能替代它。

如果下游没有围栏能力,至少把操作拆成可幂等步骤,记录任务版本和提交状态,并在每个不可逆动作前重新确认锁状态。这个办法不能消除所有竞态,但能把“重复写入”从静默覆盖变成可发现、可恢复的失败。

上线前按这张清单判断是否该用续期锁

  1. 加锁是否使用一次性 SET key token NX PX ttl,而不是分开的创建与过期命令?
  2. 续期是否在 Redis 内原子比较 token 后再执行 PEXPIRE
  3. 续期返回 0、连接失败和本地暂停时,业务是否会停止新的外部写入?
  4. 释放是否使用同样的 token 比较后删除,且不会对失败结果盲目重试?
  5. 最长任务时长、续租次数和 Redis 不可用时的恢复路径是否明确?
  6. 下游写入是否需要围栏令牌、版本号或幂等键来挡住迟到的旧 worker?

常见问题

把 TTL 调得足够长,是不是就不用续期了?

只能降低正常任务超过 TTL 的概率,不能处理进程暂停、网络隔离和异常重试。TTL 越长,锁在持有者已经消失后释放得越慢;仍要按业务可接受的恢复时间来选。

续期用 PEXPIRE,为什么还要 Lua?

单独的 PEXPIRE 不会验证 value。key 在检查和续期之间可能已经被别人重新创建,Lua 可以把比较与续期放在同一个 Redis 原子操作里。

释放脚本返回 0 要不要再删一次?

不要。返回 0 正说明当前 key 不再属于这个令牌;再次删除可能误伤后来者。记录失锁事件并让任务按幂等或补偿路径收敛更安全。

Redlock 能消除旧 worker 晚到的问题吗?

不能把它当成绝对保证。Redis 官方文档明确建议对需要强一致的资源考虑围栏令牌,并认真评估时钟、网络分区和故障恢复条件。锁算法只能覆盖锁服务本身的边界,不能自动约束所有下游写入。

把锁的边界写进验收结果

一把可维护的 Redis 分布式锁,最重要的不是“看门狗开了”,而是每个状态都有明确动作:拿不到锁就退出,续期失败就停止新的不可逆写入,释放前验证令牌,外部资源用版本或围栏令牌挡住迟到者。先用短 TTL、可观测的续期日志和故障注入测出真实边界,再决定是否需要更复杂的多节点方案。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 并发查询如何只保留一个结果:select 竞争与取消信号的收口Go 并发查询如何只保留一个结果:select 竞争与取消信号的收口
上一篇
Go 并发查询如何只保留一个结果:select 竞争与取消信号的收口
VS Code 网页版如何切换深色主题:命令面板与界面确认
下一篇
VS Code 网页版如何切换深色主题:命令面板与界面确认
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    5302次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4817次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4757次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    5024次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4963次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码