背景与痛点
- 场景:电商秒杀、库存扣减、订单幂等、任务调度、分布式定时器。
- 旧方案:MySQL
SELECT ... FOR UPDATE,性能差;单实例 Redis SETNX,单点故障导致超卖。 - 目标: 锁互斥; 高可用; 死锁可自解; 可观测;
分布式锁的三代模型
| 代数 | 实现 | 容错 | 语言生态 | 备注 |
|---|---|---|---|---|
| 1 | SETNX + EXPIRE | 0 | 所有语言 | 脚本原子性差 |
| 2 | Lua脚本(SET NX EX) | 0 | 所有语言 | 解决原子性,仍单点 |
| 3 | Redlock | N/2-1 | Java/Go/PHP | 多数派投票,官方算法 |
SETNX vs Redlock:原理与边界
SETNX 流程
CodeBlock Loading...
问题:
- 单点崩溃 ⇒ 锁信息丢失 ⇒ 并发写。
- 网络分区 ⇒ 双主脑裂 ⇒ 两个客户端同时拿到锁。
Redlock 算法(10 步精简)
- 获取当前毫秒时间戳 T1。
- 依次向 N 个独立 Redis 实例发送
SET key uuid NX PX ttl。 - 计算耗时
used = now - T1。 - 若
成功节点数 ≥ N/2+1且used < ttl,则视为加锁成功;否则向所有节点发送 DEL。 - 锁有效期 =
ttl - used,客户端需在过期前释放或续期。
数学证明:
- 在合理时钟漂移(<200 ms)下,Redlock 能提供“互斥性”与“活性”。
- 详细推导见附录 A(Redlock revisited – antirez 博文)。
Hyperf 官方 Redis 客户端能力地图
| 模式 | 连接池 | Sentinel | Cluster | Redlock |
|---|---|---|---|---|
| 支持 | ✅ | ✅ | ✅ | ❌(需扩展) |
结论:Hyperf 只做“连接”,不做“协调”。
Redlock 的数学证明与工程取舍
- 节点数 N:奇数 ≥3,推荐 5。
- 法定人数
quorum = N/2 + 1。 - 时钟漂移阈值:200 ms 内可接受。
- 网络延迟公式:
ttl > 2 * RTT_max + drift。
生产环境部署拓扑
CodeBlock Loading...
- 每台 Redis 独立部署,持久化关闭 AOF/开启 RDB 快照,避免磁盘抖动。
- 机架隔离:3 台在 A 机房,2 台在 B 机房,跨机房延迟 <2 ms。
Hyperf 落地步骤
安装
CodeBlock Loading...
多节点 Redis 配置
config/autoload/redis.php
CodeBlock Loading...
基于 hyperf-wise-locksmith 的封装
app/Service/AbstractRedLockService.php
CodeBlock Loading...
锁续期(看门狗)实现
锁续期不是 Redlock 标准的一部分,但生产必备。实现思路:
- 加锁成功后启动一个携程 Timer,周期 = ttl/3。
- 如果业务仍在执行且锁仍属于本客户端,则使用 Lua 脚本延长 ttl。
代码片段:
CodeBlock Loading...
异常捕获与重试策略
- 网络超时:抛出
LockTimeoutException,由上层重试或降级。 - 节点不可达:记录
RedisDownException,Prometheus 报警。
压测与基准数据
环境
- CPU:AMD EPYC 7K62 8C16G
- 网络:万兆以太,跨机房 1.8 ms RTT
- 工具:wrk + Lua 脚本
结果
| 并发 | 平均 QPS | 锁冲突率 | 99th 延迟 | 备注 |
|---|---|---|---|---|
| 50 | 4.8k | 1.2% | 18 ms | 无节点故障 |
| 50 | 4.5k | 1.5% | 22 ms | 随机挂 1 节点 |
| 100 | 9.1k | 2.3% | 31 ms | 随机挂 2 节点 |
结论:在 5 节点 Redlock 下,挂 2 台节点依旧保持 9k+ QPS,延迟增长 <10 ms。
故障演练与可观测性
演练脚本
CodeBlock Loading...
监控指标
redis_lock_success_total(Counter)redis_lock_fail_total{reason="timeout|quorum|node_down"}- Grafana 看板:1) 节点延迟热力图;2) 锁成功率折线。
故障甘特图
CodeBlock Loading...
常见坑 & 调优清单
| 问题 | 症状 | 解决 |
|---|---|---|
| 集群误用 | Redlock 需要独立实例,Cluster 会 hash-slot 散列 | 单独部署 5 台 |
| TTL 过短 | 业务没跑完锁就过期 | 看门狗或提高 ttl |
| 时钟漂移大 | 两台客户端同时拿到锁 | NTP + 200 ms 阈值 |
| Lua 脚本误删 | DEL 不带校验 | 使用官方脚本 |
| 大 Key | 锁 key 过长导致网络放大 | 采用短哈希 |
结论与未来展望
- 在 PHP/Hyperf 场景下,通过社区扩展即可在 1 小时内实现生产级 Redlock。
- 5 节点部署可容忍 2 台节点故障,延迟增加 <10 ms。
- 未来工作:
1) 集成 etcd 实现混合锁(Redis + Raft);
2) 提供异步锁接口,适配 ReactPHP/Swoole 长连接;
3) 将看门狗内置到扩展,减少样板代码。
附录 A Redlock 数学证明: Redlock revisited – antirez 博文
附录 B 完整源码仓库地址:https://github.com/yourname/hyperf-redlock-demo