连载中 7/20

主从切换下的锁失效:异步复制的原罪

2026-06-28 · 4687 阅读 · 0 评论 · 0 赞

一条完整的事故时间线

Redis 部署成主从加哨兵(Sentinel)是标配:主库扛写,从库兜底,主库挂了哨兵自动提升从库。一切正常时天下太平,直到某一秒,四个动作撞在了一起:

t0    客户端A 向主库 M 发 SET lock 8f3a NX PX 10000 → OK,A 拿到锁
t0+1ms 主库 M 宕机,lock 这条写还没来得及复制给从库 S
t0+30s 哨兵仲裁完成,S 升级为新主库
t0+31s 客户端B 连新主库 SET lock ... NX PX → OK(key 在新主上不存在)
      ↑ A 和 B 同时「持有」锁,临界区里出现了两个人

注意 A 并没有做错任何事:它按规矩抢锁、按规矩干活,锁丢在主从异步复制这个它根本控制不了的环节上。A 拿锁期间如果又崩又复活(第 5 篇的 GC 场景),连「感知丢锁」的机会都没有。这就是 Redis 锁最被诟病的一击,也是 Martin 批评 RedLock 时的核心论据——只要锁的持久性依赖异步复制,主从切换就永远是锁的真空时刻。这不是配置能根治的:Redis 为了写性能选择了异步复制,性能与安全的天平在架构上就已倾斜。

缓解一:min-replicas 限写

antirez 官方建议的止血方案是两个参数:

min-replicas-to-write 1     # 至少 1 个从库在线才接受写入
min-replicas-max-lag 10     # 且该从库复制延迟不超过 10 秒

思路是「与其发一把可能丢失的锁,不如不发」:主库检测到没有任何健康的从库(延迟超限)就拒绝写锁请求,抢锁的客户端直接失败。故障切换的窗口被压缩了——从「主挂了锁还在飞」变成「主挂了锁服务先停摆,新主起来再恢复」。安全性上有所改善,代价是可用性:从库抖动会连带锁服务不可用,业务要能接受「Redis 不可用时拿不到锁」的降级路径(排队、本地限流、或直接拒绝)。

缓解二:WAIT 与它的不严格

Redis 3.0 起提供了 WAIT 命令:SET 写锁成功后跟一条 WAIT 1 100(等 1 个从库确认、最多等 100 毫秒),看起来是同步复制语义。但要交底清楚:WAIT 只保证写入已进入从库的复制流,不保证从库已完成落盘,更不保证 failover 后的完整链路——WAIT 返回后主库立刻宕机,极端情况下从库升主时那条锁记录依然可能丢。WAIT 把丢锁概率从「毫秒窗口」压到「极小概率」,但压不到零,它的每一点改善都在吃写延迟。真要严格同步复制,Redis 给不了,这是定位问题不是参数问题。

Redis 锁的诚实交底

到这里,Redis 锁的底牌全部亮完,可以给它下结论了:Redis 锁是效率锁的王者,是正确性锁的赌徒。快(内存操作)、轻(不引入新组件)、生态好(Redisson 全家桶),防重复跑、防缓存击穿、防重复建单这类「双跑浪费但不致命」的场景闭眼用。但碰钱、碰库存、碰任何「双跑即事故」的场景,单靠它就是赌——要么赌主从切换不撞上临界区(多数时候能赢),要么配上 fencing token 兜底(第 16 篇),要么换一把「天生为一致而生」的锁。第 5 篇那张选型表的最后一行,就是接下来四篇的主角:ZooKeeper——一个把「所有节点对同一件事达成一致」写进设计目标、用 ZAB 协议背书的协调服务。下一篇先补齐它的基础课:ZNode、Watch、临时节点,三个概念撑起它的全部精彩。

Cluster 分片:同一出戏演 N 遍

有人会想:换成 Redis Cluster 是不是就好了?答案是原样重演。Cluster 只是把数据按 slot 分片到多个主从组,每一把锁落在某个 slot 上,就只归那一个分片的主库管——每个分片内部依然是异步复制的主从结构,主从切换时丢锁的窗口一秒都没少。分片多反而让「哪把锁在哪片」的心智负担更重。一句话总结 Redis 锁的复制观:单机、哨兵、Cluster,三种架构三种壳,异步复制的芯一模一样。要跟这种原罪正面硬刚,只能靠 RedLock 的多数派、或下一篇登场的 ZAB 与 Raft 这类真协议。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#分布式锁#主从切换#异步复制#哨兵#min-replicas#丢失锁

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10100 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9872 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9293 阅读 · 21 评论 · 287 赞