如果光缆被挖断导致 Redis 出现两个 Master,怎么防止数据丢失?
当前时间:2026年4月23日新加坡。案发场景你们的支付系统使用了极其标准的 Redis 高可用架构1 个 Master2 个 Slave外加 3 个 Sentinel哨兵。Master 部署在机房 ASlave 和哨兵部署在机房 B。客户端应用也部署在机房 A。灾难降临网络分区的死神晚上 10 点机房 A 和机房 B 之间的主干光缆被挖掘机挖断了发生了网络分区。精神分裂开始机房 B 的视角3 个哨兵发现联系不上 Master 了它们立刻举行紧急会议Raft 选举把机房 B 里的一个 Slave 提拔成了新的 Master。机房 A 的视角原本的老 Master活得好好的。并且客户端应用和老 Master 处在同一个机房网络是通的此时你的宇宙里出现了两个 Master脑裂客户端浑然不知继续疯狂地把大量的支付订单写入了机房 A 的老 Master。惨剧的终章破镜难重圆10 分钟后光缆修好了。机房 A 和 B 重新连通。哨兵系统巡视了一圈发现了尴尬的一幕怎么有两个 Master哨兵残酷地降级了机房 A 的老 Master逼迫它变成新 Master 的 Slave。大屠杀发生按照 Redis 的主从同步铁律老 Master 在变成 Slave 的那一刻会立刻清空自己的所有内存去全量复制新 Master 的数据在光缆断裂的这 10 分钟内客户端写入老 Master 的几十万笔订单数据瞬间灰飞烟灭永远消失连 AOF 日志都救不回来。面对这种极其残忍的数据丢失架构师们必须祭出极其克制的防御手段。1. 核心原理解剖为什么 Redis 会丢数据很多初级研发会问“既然老 Master 接收了数据网络恢复后它把这 10 分钟的数据推送给新 Master 不就行了吗”这是关系型数据库如 MySQL 双主架构或者 Git 合并分支的思维。但Redis 绝对不支持多活写冲突合并Redis 的设计哲学是极度追求吞吐量异步复制机制客户端向 Master 写入SET order:10086 paidMaster 瞬间把数据写进内存不等待 Slave 确认直接向客户端返回OK。独裁覆写原则在主从架构中Master 是绝对的真理。Slave 连上 Master 的第一件事就是无脑丢弃自己不同的数据完全以 Master 的 RDB 快照为准。正因如此当脑裂恢复时系统只能认准一个“真理”。而由于哨兵提拔了新 Master老 Master 就成了叛军它的数据必须被无条件清洗。2. 终极防御min-replicas的双刃剑既然数据在恢复时注定会被清洗那我们唯一的防守策略就是在脑裂发生网络断开的期间强行禁止客户端向老 Master 写入数据Redis 为此提供了两个极其硬核的保命配置在 Redis 5.0 之前叫min-slaves-to-write5.0 之后改名在老 Master 的redis.conf中配置# 要求 Master 至少有 1 个 Slave 保持正常连接 min-replicas-to-write 1 # Master 和 Slave 之间失去联系的延迟时间不能超过 10 秒 min-replicas-max-lag 10防御工作流推演回到刚才光缆被挖断的 10 分钟光缆断裂老 Master 发现自己的两个 Slave 都没有按时发送ACK心跳。过了10秒钟老 Master 触发了min-replicas-max-lag的底线。此时虽然客户端还在机房 A 疯狂向老 Master 发送SET命令但老 Master 直接拒绝服务它会向客户端抛出一个冷酷的错误NOREPLICAS Not enough good replicas to write.客户端写入失败抛出异常随后触发业务层的降级比如把消息暂存到本地或发到 MQ。10 分钟后网络恢复老 Master 被降级并清空。但由于它之前拒绝了所有的写入所以根本没有丢失任何有效数据3. CAP 定理的无情制裁保命的代价你以为配上这两个参数就万事大吉了天下没有免费的午餐。当你开启min-replicas-to-write时你实质上是在CAP 定理一致性、可用性、分区容错性中放弃了可用性Availability选择了强一致性Consistency。架构师的灾难推演新的风暴假设你的架构是 1 主 1 从为了省钱。你配置了min-replicas-to-write 1。某天网络没有任何问题仅仅是你的Slave 机器主板烧了宕机了。此时你的 Master 活得好好的但它发现自己唯一的一个 Slave 失去了联系lag 10秒。结果Master 为了遵守配置规则当场“自爆”拒绝所有客户端的写请求仅仅死了一个从库却导致整个系统的写入功能全部瘫痪实战权衡铁律如果你用 Redis纯做缓存绝对不要开启这个参数脑裂丢了缓存就丢了大不了去 MySQL 里重新捞。可用性活着大于一切。如果你用 Redis做分布式锁、或者发号器、核心库存扣减强烈建议开启宁可让用户在几分钟内无法下单报错也绝对不能让同一把锁被两个人同时拿到导致严重的资金对账灾难。如果必须开启请确保至少有 3 个数据节点1 主 2 从配置min-replicas-to-write 1。这样即使死了一个从库还有一个从库能保底防止 Master 误杀自己。4. 彻底解决脑裂的终极方案Redlock 或外部协调组件其实无论怎么调整min-replicas只要 Redis 本质上依然是异步复制的 AP高可用模型它就无法从根本上解决网络分区带来的“脏读脏写”问题。如果你的业务对数据一致性要求达到了**“金融级绝对正确”**的苛刻程度不要用 Redis Sentinel/Cluster 去扛强一致性场景引入基于Raft 或 Paxos 共识算法的分布式协调系统如ZooKeeper或etcd。在 ZK 中写入数据必须得到**半数以上Majority**节点的确认。当发生网络分区脑裂时被隔绝的老 Leader 节点由于无法收集到半数确认天然无法执行任何写操作从物理学上绝对杜绝了脑裂数据丢失的可能。总结Redis 脑裂是一场由于“网络光缆”和“异步复制机制”共同上演的黑色幽默。当我们享受 Redis 单机数十万并发写入的极速快感时我们实际上是把“数据一致性”作为筹码换取了这种极致的速度。min-replicas配置就像是给这辆狂奔的跑车装上了一个安全气囊当系统发现自己驶入未知的浓雾网络分区时强制踩死刹车牺牲掉这段时间的可用性换取车毁人亡前的最后生机。