脑裂数据丢失路径

脑裂数据丢失路径

脑裂导致数据丢失的完整过程

脑裂导致的数据丢失不是瞬间发生的,而是一个逐步演进的过程。理解这个路径有助于针对性地设计防护措施。

第一阶段:网络分裂

正常状态:
主节点 A ←→ 从节点 A1、A2  ←→ 其他主节点 B、C
所有节点通过网络正常通信。

脑裂触发:
主节点 A 与其他节点(包括它的从节点 A1、A2)之间的网络中断。
但 A 与客户端的连接可能仍然正常(取决于网络拓扑)。

此时,从 A 的视角看:从节点和其他主节点都不可达了。但从客户端视角看:A 仍然是可用的。

第二阶段:数据分岔(Divergence)

网络中断后,集群中的节点开始数据分岔

旧主节点 A 侧

  • 客户端仍然通过 A 的 IP 连接 A
  • 客户端写入的数据都保存在 A 上(但无法同步到任何从节点)
  • A 的 replication offset 无法增长
  • A 的 repl_backlog 只记录本地变更

新主节点 A1 侧

  • 其他主节点通过 Gossip 发现 A 不可达,标记为 PFAIL → FAIL
  • 如果有半数以上主节点同意,触发从节点 A1 晋升
  • A1 成为新主节点,开始接受客户端写入
  • 从节点 A2 切换到 A1 的从节点

数据变化

A 上写入的 Key,在 A1 上不存在
A1 上写入的 Key,在 A 上不存在

第三阶段:网络恢复,发现冲突

当网络恢复后,A 重新能够与集群中的其他节点通信:

握手过程

  1. A 发送 PING 给其他节点
  2. 其他节点回复 PONG,告知 A 当前的集群状态
  3. A 发现集群中已经存在一个”主节点 A1″
  4. A 发现自己的 config epoch 比 A1 小(A1 晋升时 epoch 会递增)

角色宣布

Redis Cluster 的冲突解决规则非常简单:epoch 大的节点拥有更高的权威。A1 的 epoch 大于 A,因此:

  • A 被集群告知它不再是主节点
  • 集群强制将 A 降级为从节点
  • A 开始执行 SLAVEOF A1,复制 A1 的数据

第四阶段:数据丢失

这是最关键的一步。当 A 被降级为从节点后:

清空自身数据

从节点在开始全量复制之前,会先执行 FLUSHALL(清空所有数据):

A 上的数据:
├── 与 A1 一致的数据(网络中断前同步的)
├── 脑裂期间客户端写入 A 的数据  ← 这一部分将要丢失
└── A 的执行日志(包含脑裂期间的写入操作)

全量同步

  1. A 向 A1 发送 PSYNC 请求
  2. A1 生成 RDB 快照发送给 A
  3. A 加载 RDB,覆盖所有本地数据

结果:A 上的所有数据被 A1 的数据覆盖,脑裂期间在 A 上写入的 Key 全部丢失。

丢失数据的量化分析

最大丢失量

最大丢失数据量 = 脑裂持续时间 × 主节点写入 QPS × 平均 Key 大小

脑裂持续时间的上界 ≈ cluster-node-timeout(默认 15 秒)

例如:
– QPS = 10000 写入/秒
– 脑裂持续 ≈ 15 秒
– 平均每个 Key 100 字节
– 最大可能丢失 ≈ 15 × 10000 × 100 ≈ 15 MB

实际丢失量

实际丢失量通常小于理论值,因为:
1. 故障发现和选举需要时间(不是瞬间提升从节点)
2. 旧主节点在检测到与半数主节点失联后,可能主动停止写入(取决于配置)
3. 客户端可能同时感知到故障并切换连接

四种无法挽回的数据丢失场景

  1. 异步复制延迟丢失:主节点写入成功但尚未同步到从节点时宕机
  2. 脑裂写入丢失:脑裂期间写入旧主节点的数据在恢复后被覆盖
  3. 选举期间写入丢失:主节点宕机到从节点晋升期间,客户端写入失败的数据
  4. 全量同步覆盖丢失:旧主节点被降级为从节点时的全量同步

总结

脑裂数据丢失的路径可以概括为:网络分区 → 数据分岔 → 角色降级 → 全量同步覆盖。关键在于第三步”角色降级”后,旧主节点以 FLUSHALL + 全量同步的方式清除了脑裂期间写入的所有数据。理解这条路径后,就能明白为什么 min-replicas-to-writemin-replicas-max-lag 能在网络分裂前就阻断写入路径,从而从源头防止数据丢失。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容