Redis 脑裂与 min-replicas-to-write 写保护

举报
yd_216253085 发表于 2026/11/13 07:31:02 2026/11/13
【摘要】 讲清异步复制的丢失窗口、脑裂成因与写保护参数边界

Redis 的复制是异步的,主节点写入内存即返回成功,不等从节点确认。这个设计换来了极低的写延迟,也埋下了一个绕不开的问题:网络分区时,被隔离的旧主节点仍在接收写入,而这些数据最终会被丢弃。本文基于 Redis 7,讲清丢失窗口的成因、脑裂的完整链路,以及 min-replicas-to-write 写保护的真实能力边界。

一、异步复制为什么必然存在丢失窗口

主从复制分两步:主节点执行命令并回复客户端,随后把命令异步传播给从节点。任何一次「主节点故障 → 从节点提升」的切换,提升上来的从节点只拥有它已经收到的数据,主节点最后几百毫秒到几秒的写入就不在了。这不是实现缺陷,而是协议设计——想消除它只能引入同步复制,而同步复制会摧毁 Redis 的性能优势。官方的立场很明确:只能在窗口大小上做文章,无法归零。

窗口的大小可以量出来。主从之间的 offset 差值就是"还没传过去的数据量",除以写入速率就是时间:

redis-cli -p 6379 info replication | grep -E "master_repl_offset|slave0"
# master_repl_offset:88213904
# slave0:ip=10.0.1.21,port=6379,state=online,offset=88213102,lag=0

两者相差 802 字节,按当时约 40 KB/s 的写入速率换算,落后不足 20 毫秒。这个数字很小,但它只代表采样瞬间;真正的风险在故障时刻——如果主节点恰好在一次批量写入后立刻宕机,这部分数据就整体丢失。所以要估算 RPO 时,不能看平时的 offset 差值,而要看峰值写入速率乘以"传播延迟"。峰值 5 MB/s、传播延迟 30 毫秒时,一次切换最多丢 150 KB,约等于几千条小命令。

二、脑裂是怎么发生的

以哨兵架构为例。网络分区把集群切成两半:旧主节点和一部分客户端在 A 区,从节点与多数哨兵在 B 区。哨兵们把某个从节点提升为新主,B 区业务正常;A 区的客户端不知道拓扑已变,继续向旧主写入。分区恢复后,旧主被降级为从节点做全量同步,自己在分区期间接收的写入全部蒸发。

举个数字例子:业务写 QPS 2000,分区持续 60 秒,A 区被写入 12 万条命令,恢复后这些数据一并丢失。Cluster 架构下机制相同——故障主节点的从节点经多数派投票晋升,被隔离的旧主同样继续收写,同样在回归时被清空。

脑裂的完整链路可以拆成五个阶段,理解每个阶段才能知道在哪里下刀:

  1. 分区形成:旧主与从节点之间的网络断开,但旧主与客户端的连接完好;
  2. 判定下线:B 区的哨兵(或 Cluster 的多数主节点)在 down-after 或 node-timeout 后判定旧主失败;
  3. 新主诞生:B 区提升一个从节点,新主开始接受 B 区客户端的写入;
  4. 双写并行:A 区客户端仍连旧主并写入成功,此时系统存在两个"主",数据分叉;
  5. 回归清空:分区恢复,旧主被降级并执行全量同步,第 4 阶段写入的数据被覆盖。

要在第 4 阶段之前介入,唯一可用的手段是让旧主自己停止写入——因为它无法知道 B 区已经选了新主,只能通过"有没有健康从库"这个侧面信号来判断自己是否还在一个正常的复制链上。这就是写保护参数的切入点。

三、min-replicas-to-write 写保护

Redis 提供两个参数把丢失窗口压窄:

min-replicas-to-write 1
min-replicas-max-lag 10

语义是:主节点要求在最近 min-replicas-max-lag 秒内,至少有 min-replicas-to-write 个从节点上报过复制进度(从节点每秒通过 REPLCONF ACK 上报 offset);条件不满足时主节点拒绝写入,返回错误:

(error) NOREPLICAS Not enough good replicas to write.

读操作不受影响,Cluster 模式下每个主节点也独立执行同样的判断。回到第二节的场景:配置生效后,旧主在失去从节点 10 秒内就自断写入,A 区客户端只能拿到连接错误而不是「成功写入后丢失」,丢失窗口从整个分区时长压缩到 max-lag 秒以内。

四、写保护不是免死金牌

第一,窗口仍在,只是变小了。旧主在收到最后一个 ACK 之后的 max-lag 秒内仍会接受写入,这段数据照样会丢;把 max-lag 调到 1 秒可以进一步收窄,但正常复制抖动也会触发拒写。第二,可用性有代价:从节点全部下线(比如滚动重启维护)时,主节点会拒写,业务直接失败——维护窗口前要临时调小或置 0。第三,它防的是「旧主继续写」,不防「新主已提升后旧主复活双主」的场景,后者由哨兵降级旧主、Cluster 用纪元仲裁解决。第四,对金额、库存这类不可丢数据,治本方案仍是落库加缓存定位,或者选用支持同步复制的存储。

对单条关键写入,还可以用 WAIT 命令阻塞等待指定数量的从节点确认,但它有毫秒到秒级的延迟成本,且只保证命令送达,不保证已执行,只适合低频关键路径。

常见坑 / 最佳实践

  1. 给单从节点实例配置 min-replicas-to-write 1 前先评估。 从库重启的几分钟里主库拒写,业务可能大面积报错。变更前临时调整,维护完再恢复。
  2. max-lag 不要小于 5 秒。 1 到 2 秒的阈值在正常写入高峰都可能触发,建议从 10 秒起步观察 INFO replication 的延迟情况再收紧。
  3. 不要误以为 Cluster 不受这两个参数影响。 每个主节点独立判断,某个分片的从库断链时,只有该分片拒写——排查「部分接口写失败」时要想到这一层。
  4. 把 NOREPLICAS 错误接入告警。 它是写保护生效的信号,也往往是脑裂正在发生的第一现场,比事后对账更早发现问题。
  5. 关键数据不要只存 Redis。 写保护只能收窄丢失窗口,账务、库存类写入必须同步落库,Redis 仅作加速层。

小结

  • 异步复制决定了切换必有丢失窗口,这是协议设计而非缺陷,目标只能是收窄。
  • 脑裂的本质是分区两侧各有「能服务的写入方」,恢复后旧主降级清空期间数据。
  • min-replicas-to-write 加 min-replicas-max-lag 让失去从节点的主节点在 max-lag 秒内自断写入,把窗口压到秒级。
  • 写保护有可用性代价,维护窗口要临时关闭;Cluster 下按分片独立生效。
  • NOREPLICAS 是脑裂的第一信号,值得单独告警;不可丢数据始终要落库兜底。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。