Redlock 大概是分布式系统里被讨论得最多、被真正用得最少的一种锁。antirez 和 Martin Kleppmann 2016 年那场论战把它的尴尬摆上了台面,我的看法很直接:Redis 做分布式锁就单实例加看门狗,要正确性就上 etcd,Redlock 卡在中间两头不靠,别选它。这篇文章把三种方案的真实边界讲清楚,顺便告诉你一把合格的 Redis 锁到底怎么写。

一把合格的 Redis 锁长什么样

先写对再说。90% 的"分布式锁不靠谱",问题不在 Redis,在那三行代码。

加锁:

SET lock:order:1001 7f3a9c2e-1b64-4d5a NX PX 30000

释放:

if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end

这四行里有三个不能省的点。

value 必须是每次生成的唯一串(UUID 就行)。为什么?释放锁的时候要先确认"这把锁还是不是我的"。删库容易,删对难——value 就是你的身份证。我见过把 value 写成固定字符串的代码,两个进程互相删对方的锁,删得理直气壮,超卖照旧。

释放必须用 Lua 原子比对再删。先 GET 再 DEL 两步走,中间夹一次网络往返,恰好在你 GET 完之后锁过期、别人拿到了锁,你的 DEL 就删到了别人头上。Lua 脚本在 Redis 里原子执行,这个窗口不存在。SET 的 NX PX 一个命令同理,别用 SETNX 加 EXPIRE 两步拼——两步之间进程挂了,你就得到一把永不过期的锁,全员给你陪葬。

过期时间必须有。没有 PX 的锁在持锁进程崩溃时就是死锁。

就这些。单实例 Redis 上,这四行代码给你的保证是:同一时刻只有一个客户端持有锁,锁最多 30 秒自动释放,不会误删。对绝大多数场景——防重复提交、定时任务防多跑、缓存击穿加锁回源——这套保证够用了。

过期时间定多少

经典权衡:定短了,业务没跑完锁就被别人抢走,等于没锁;定长了,持锁进程崩了,所有人干等一个死人。

Redisson 在 Java 圈给出的答案是看门狗:默认 30 秒过期,后台每 10 秒续一次期,进程活着就一直续,进程死了最多 30 秒自动放手。这个设计把权衡变成了"你不用再选",我个人认为它是整个 Redis 锁生态里最值得抄的一个设计。

PHP 这边没有官方看门狗,php-fpm 的请求结束进程就还回去了,没有常驻的东西帮你续期。所以 PHP 圈的共识是另一个方向:临界区切短,锁内只做必须互斥的那几条 SQL,5 秒够不着的那种,别把调第三方接口、发邮件这种慢活塞进锁里。这其实是更普适的原则——Java 有看门狗也不该把慢活锁进去。

Redlock 到底在解决什么问题

单实例锁的软肋:Redis 主从是异步复制的。你在 master 上加锁成功,master 还没把这条写同步给 slave 就宕机了,slave 升级成新 master,锁没了,第二个客户端在新 master 上加锁成功。两个客户端同时持锁。

Redlock 的方案:部署 5 个完全独立的 Redis 主节点,加锁时依次向 5 个节点请求,拿到多数派(至少 3 个)且总耗时小于锁有效期,才算成功。

听起来很稳。然后 Kleppmann 2016 年那篇《How to do distributed locking》把它拆了:客户端 1 拿到锁之后发生了一次长时间 GC 停顿(或者时钟跳变),停顿期间锁过期,客户端 2 拿到锁开始写入,客户端 1 苏醒,自认为还持着锁,也开始写入——共享存储被两个"持锁者"先后写坏。Redlock 对此无能为力,因为它给不出 fencing token——一个单调递增的令牌,由共享存储端校验、拒绝旧持有者的写入。antirez 的反驳围绕 GC 停顿在实践中的可控性、Redis 的实现细节,也承认 fencing token 需要 Redis 之外的支持。

这场架吵到今年整整十年,没有胜负。我认为没有胜负本身就是答案:一个理论上吵不出正确性、实践上要多养 5 个独立节点的方案,它解决的问题(Redis 挂了锁不能丢),用 etcd 的租约加 session 更直接地解决了,还是 CP 系统。

真实经历佐证一下"标识对了链路才通"这件事有多容易翻车:我这个博客上线 Markdown 支持时踩过一次坑,文章入库了、正文也在 contents 表里躺着,前台就是不渲染,一坨原始 ## 和 ** 糊在页面上。排查半天发现是入库时漏传了 content_type 字段——前台靠它决定当 Markdown 还是富文本解析。上游差一个标识,下游全盘皆错,锁的 value 是同一个道理,只是它的代价不是渲染错乱,是你删了别人的锁、或者拿着一把已经不存在的锁在写数据。

那什么时候该换掉 Redis 锁

场景 推荐 理由
防重、防缓存击穿、任务防多跑 单实例 Redis + 唯一 value + Lua + 看门狗 锁偶尔丢了没有实际损失,简单压倒一切
扣库存、转账、订单状态机 etcd/ZooKeeper + fencing token,或数据库唯一约束兜底 锁丢了就是超卖和资损,正确性压倒一切
多机房、Redis 本来就是集群 etcd Redlock 的"独立节点"在云上经常是假的

注意第二行:真正该兜底的从来不是锁,是锁外面的东西——数据库唯一索引、幂等表、状态机的条件更新(UPDATE ... WHERE status=1)。锁只是减少无效竞争的优化手段,谁拿锁当唯一正确性防线,谁迟早超卖一次。

收个尾

Redlock 这个方案,我赌它会在接下来几年里慢慢淡出讨论——新项目要正确性直接 etcd,要简单单实例加看门狗,它卡在中间的位置一年比一年尴尬。antirez 自己后来主推的功能里也没有它的位置。你要是现在手上有 Redlock,先去看看那五个节点是不是真的独立部署的;凑数的多数派,比单实例还危险,因为它给了你一种"我做了高可用"的错觉。

艾林博客 - 技术分享、开发经验与AI探索的个人技术博客
艾林博客 - 技术分享、开发经验与AI探索的个人技术博客

延伸阅读:

PHP 8.6 的性能头号卖点,在八月被撤回了 开发编程
PHP 8.6 的性能头号卖点,在八月被撤回了

PHP 8.6 的闭包优化 RFC 以 24:0 通过,但 static 自动推断在 8 月被撤回,那个 3% 只对显式写了 static 的闭包生效;同时 35 项弃用投票有 4 项被否,list() 以 23:23 保住。我把自己的 626 个 PHP 文件扫了一遍,自有代码零命中,升级计划随之改掉。

PHP 后端

Valencio

/

2026-10-16

内存条为什么涨了两倍多:AI 抢的不是内存,是晶圆 系统运维
内存条为什么涨了两倍多:AI 抢的不是内存,是晶圆

两根内存条从 700 多涨到 2000 多,根子不在缺货,在晶圆面积:HBM 每产出 1 bit 要占标准 DDR 三倍的硅片,2026 年吃掉 22% 的 DRAM 产能只交回 9% 的容量。再叠加 60%~70% 的服务器级产能被长协锁死、新产能要 2028 年才算数,这轮周期不会像 2019 年那样崩回去。文章同时给出云账单与内存预留的排查清单。

优化 AI

Valencio

/

2026-10-13

GPT-6.1 Sol 发布:五分之一价格拿到 Astra 能力,同一周更强的 Astra 却被取消了 AI与大模型
GPT-6.1 Sol 发布:五分之一价格拿到 Astra 能力,同一周更强的 Astra 却被取消了

GPT-6.1 Sol 用约五分之一的价格拿到接近 Astra 的成绩,但同一周更强的 GPT-6.1 Astra 被取消发布,理由是欺骗性汇报与越权执行。本文拆解三档版本关系、缓存降价与推理档位收紧的真实影响、取消事件的时间线,以及生产环境 Agent 防范假完成报告与越权执行的四道工程防线。

AI 后端 安全

Valencio

/

2026-10-08

GPT-6 Sol 和 Luna 发布:价格砍到 Astra 的 1%,但按价签选模型会踩坑 行业快讯
GPT-6 Sol 和 Luna 发布:价格砍到 Astra 的 1%,但按价签选模型会踩坑

9 月 22 日 OpenAI 补上 GPT-6 的 Sol 和 Luna 两档,价格降到 Astra 的 1/5 和 1/100。但重点不是便宜:三档能力差距按工作类型分叉——编码任务上 Luna 只落后 Sol 2.2 分,智能体任务上却掉 12.5 分。本文附三档规格对比、计费隐藏条款、GPT-6 Sol 与前代的代际对比,以及一段可直接抄走的 PHP 模型路由实现。

AI 后端

Valencio

/

2026-09-23