主从切换与版本升级同时进行极易引发脑裂,因升级中断复制、改变gtid状态或锁主库,而切换要求数据完全一致;稍有延迟或配置错位(如旧主未离线、新主已写入),即触发双写、主键冲突或唯一索引报错。

主从切换和版本升级同时进行时,脑裂不是“可能”发生,而是只要有一处检查漏掉,就大概率发生。核心矛盾在于:升级过程会中断复制链路、改变GTID状态或锁住主库,而切换操作又要求数据完全一致——两者叠加,稍有延迟或配置错位,旧主未彻底离线、新主已开放写入,双写即刻触发主键冲突或唯一索引报错。
为什么升级中切主极易引发脑裂
升级不是原子操作,主库停写、从库升主、新主启服、旧主重连这四个阶段之间存在时间窗口,每个窗口都可能被误写或误判:
-
FLUSH TABLES WITH READ LOCK后若未立刻记录SHOW MASTER STATUS就断开连接,锁自动释放,新事务写入但位置未被捕获,切主后这部分 binlog 永远丢失 - 从库升级后启动时,若
super_read_only=OFF且未及时执行SET GLOBAL read_only = OFF,应用流量切过去却报错“read_only is enabled”,运维慌乱中手动关read_only,但忘了关super_read_only,结果部分语句能执行、部分被拦,状态混乱 - GTID 模式下,新主启动后没清空
gtid_purged或设置错误,旧主重连时因 GTID 冲突拒绝 IO 线程,DBA 为“快速恢复”强行RESET MASTER,导致后续所有从库同步失败并开始重复执行事务
必须在切换前验证的三个硬性条件
这些不是“建议”,是脑裂防火墙的物理边界。少一个,就等于在隔离墙上开了扇门。
- 所有待升级从库的
Seconds_Behind_Master = 0,且Slave_SQL_Running_State显示 “Slave has read all relay log”,不能只看Slave_IO_Running = Yes - 用
SELECT GTID_SUBSET(@@global.gtid_executed, 'xxx')=1校验每台从库的Executed_Gtid_Set是否完全包含主库当前gtid_executed;若不全,升级前必须等追平,不能跳过 - 确认所有节点
super_read_only = ON且read_only = ON(SHOW VARIABLES LIKE '%read_only%'),这是防止误写的最后一道开关,升级脚本里必须显式检查并修复
升级+切换过程中最易踩的配置坑
很多脑裂不是逻辑错误,而是参数被静默覆盖或版本兼容性断裂:
- MySQL 8.0.23+ 默认开启
binlog_transaction_compression = ON,老版本从库无法解压,Seconds_Behind_Master会变成NULL,Slave_SQL_Running_State卡在 “Waiting for transaction to be committed”——这不是延迟,是协议级拒绝,必须在新主上临时关掉该参数 - 跨大版本升级(如 5.7 → 8.0)时,
enforce_gtid_consistency = ON必须在主库和所有从库都生效,否则 DDL 事务在复制层直接被拦截,SQL 线程报错ER_GTID_UNSAFE_STATEMENT并停止 - 升级后配置文件里残留废弃参数,比如
innodb_locks_unsafe_for_binlog,mysqld 启动失败或降级为兼容模式,导致复制行为异常,这类问题在error.log里只报 warning,容易被忽略
切完主之后必须立即做的三件事
切换完成不等于安全,真正危险的是“以为切完了”的松懈时刻:
- 在新主库上立刻执行
CLUSTER.fenceWrites()(MySQL 8.0.31+),它会强制启用super_read_only并禁用自动管理,堵死任何意外写入路径 - 在旧主库重连前,先用
pt-table-checksum对比核心表,别信COUNT(*)——隐式类型转换或排序规则变更会让行数一致但内容错位 - 所有从库重新指向新主后,不要只查
Slave_IO_Running = Yes,必须等Seconds_Behind_Master = 0且Retrieved_Gtid_Set = Executed_Gtid_Set才算真正追平
脑裂从来不是突发事故,它是多个“小疏忽”在时间轴上串成一条链的结果。最常被跳过的,其实是升级前对 super_read_only 的强制校验,和切换后对 CLUSTER.fenceWrites() 的调用——这两步不耗时,但缺一不可。











