Redis 集群脑裂深度剖析:成因、危害与防丢失策略
1. 引言
在 Redis 高可用架构中,主从复制 + 哨兵(Sentinel)模式为我们提供了自动故障转移的能力。然而,在分布式系统中,网络并不可靠------脑裂(Split-brain)便是在网络分区环境下可能出现的严重问题。一旦发生脑裂,可能导致大量数据丢失,甚至让恢复后的集群状态混乱。
本文将从脑裂的成因、带来的数据丢失问题,到如何通过合理的配置最大限度降低损失,一步步为你剖析这个"隐形杀手",并提供可落地的解决方案。
2. 什么是脑裂?
脑裂(Split-brain)是指在分布式系统中,由于网络故障,原本统一的集群分裂成两个或多个相互独立的"派系",每个派系都认为自己是正常的,并继续对外提供服务。
在 Redis 主从 + 哨兵集群中,脑裂的典型表现是:同时存在两个 Master 节点,各自接收写请求,导致数据无法合并。
2.1 脑裂发生场景示意图
脑裂状态
正常状态
复制
复制
心跳
心跳
心跳
无法连接
检测到原 Master 主观下线
检测到原 Master 主观下线
检测到原 Master 主观下线
客户端仍写入
正常
正常
正常
Master
Slave1
Slave2
Sentinel1
Sentinel2
Sentinel3
原 Master
网络隔离区
被选举的新 Master
3. 脑裂的形成原因
脑裂通常由以下条件同时触发:
- 网络分区:主节点(Master)与从节点(Slave)、哨兵(Sentinel)之间的网络发生故障,导致 Master 被隔离。
- 哨兵误判 :哨兵集群因为无法收到 Master 的响应,将其标记为主观下线 (SDOWN),随后达到客观下线(ODOWN)阈值,触发故障转移。
- 选举新 Master:哨兵从健康的 Slave 中选举出一个新的 Master。
- 客户端未切换:原 Master 仍然存活,并且客户端未及时更新 Master 地址,继续向原 Master 写入数据。
结果:两个 Master 同时接受写请求,数据分道扬镳。
4. 脑裂导致的严重后果
4.1 数据丢失
当网络恢复后,哨兵会将原 Master 降级为 Slave,并强制其从新 Master 同步数据。由于原 Master 在隔离期间写入的新数据无法传递给新 Master,这些数据将被永久丢弃。
数据丢失量:取决于网络分区持续的时间以及原 Master 在此期间写入的数据量。这对于金融、订单等业务可能是灾难性的。
4.2 数据不一致
即使原 Master 没有被立即降级(例如哨兵配置了 down-after-milliseconds 较大),两个 Master 同时写入也会造成双方数据不同步。恢复时需要人工介入合并,极其复杂。
4.3 脑裂的时序图
原 Slave 新 Master 哨兵集群 原 Master 客户端 原 Slave 新 Master 哨兵集群 原 Master 客户端 网络分区发生 网络恢复 隔离期间写入的数据丢失! 心跳丢失 判断客观下线 提升为 Master 继续写入(脑裂期间数据) 正常复制 降级为 Slave,并指向 M_new 全量同步(清空本地数据)
5. 如何防止脑裂导致的数据丢失?
Redis 提供了两个关键的配置参数,用于限制 Master 在异常情况下的写入行为,从而减少数据丢失。
5.1 min-slaves-to-write 与 min-slaves-max-lag
这两个参数通常在 redis.conf 中配置,用于要求 Master 在写入前检查与 Slave 的复制健康状态。
| 参数 | 含义 | 推荐值 |
|---|---|---|
min-slaves-to-write |
Master 至少需要连接的 Slave 数量(不包括自己) | 根据 Slave 总数设定,例如 3 个 Slave 时可设为 2 或 3 |
min-slaves-max-lag |
Slave 与 Master 的最大允许延迟(秒) | 10(单位:秒) |
工作原理:
- 当 Master 上的 Slave 数量 小于
min-slaves-to-write,或者 任一 Slave 的复制延迟超过min-slaves-max-lag时,Master 会拒绝执行写命令,并向客户端返回错误。 - 这样,在脑裂场景下,被隔离的原 Master 无法满足"至少 N 个 Slave 正常复制"的条件,因此它会拒绝客户端写入,从而避免产生孤立数据。
5.2 配置示例
conf
# redis.conf
# 至少要有 2 个 Slave 连接正常,且延迟不超过 10 秒,否则 Master 禁止写入
min-slaves-to-write 2
min-slaves-max-lag 10
注意 :如果 Slave 总数不足 min-slaves-to-write,Master 将始终拒绝写入,因此该值应小于等于实际 Slave 数量。
5.3 该配置也能防止异步复制丢数据
即使在没有脑裂的普通异步复制场景下,如果 Master 突然宕机,尚未发送给 Slave 的写命令也会丢失。通过 min-slaves-max-lag 可以控制数据丢失窗口:
- 设置
min-slaves-max-lag 10意味着 Master 会等待 Slave 的 ACK 延迟不超过 10 秒,否则拒绝写入。 - 这相当于将异步复制转变为半同步复制(但非严格同步),牺牲部分可用性换取数据一致性。
6. 其他防御措施与最佳实践
6.1 客户端配置
- 使用支持哨兵模式的客户端(如 Jedis、Lettuce),并启用自动故障切换。
- 设置合理的连接超时 和重试策略,避免客户端长时间连接失效的 Master。
6.2 哨兵配置调优
down-after-milliseconds:不宜过大(否则故障转移慢),也不宜过小(容易误判),推荐 30s。failover-timeout:故障转移超时时间,根据网络情况调整。- 部署奇数个哨兵(≥3),并分散在不同物理机上。
6.3 网络层加固
- 使用可靠的内网/专线连接,避免公网不稳定。
- 开启 Redis 的
tcp-keepalive,及时发现死连接。
6.4 监控与告警
- 监控 Redis 的
master_link_status、connected_slaves等指标。 - 当
connected_slaves低于min-slaves-to-write时触发告警。
7. 总结
| 问题 | 原因 | 后果 | 解决方案 |
|---|---|---|---|
| 脑裂 | 网络分区 + 哨兵自动切换 + 客户端未切换 | 数据丢失、数据不一致 | 配置 min-slaves-to-write 和 min-slaves-max-lag,拒绝危险写入 |
| 异步复制丢数据 | Master 宕机时命令未同步到 Slave | 少量数据丢失 | 同样通过 min-slaves-max-lag 限制写入滞后 |
核心思想 :在分布式系统中,可用性 与数据一致性需要权衡。通过上述配置,我们牺牲了部分写入可用性(当 Slave 不健康时 Master 拒绝服务),换取了脑裂场景下的数据安全。
8. 思考与练习
- 如果设置
min-slaves-to-write = 3,但实际只有 2 个 Slave,会发生什么? - 脑裂恢复后,原 Master 上未同步的数据是否可以通过某种手段找回?
- Redis Cluster 原生模式是否也会出现脑裂?其处理方式有何不同?