脑裂发生后必须先停写、比对GTID集合确认数据完整性,再以备份为基准回放binlog还原可信主库至脑裂前状态,逐表人工比对冲突行并业务语义合并,最后重建集群并禁用自动故障转移。

脑裂发生后,不能直接“选一个主库继续用”,必须先确认哪边的数据更完整、哪边有不可逆写入冲突。备份在这里不是兜底恢复手段,而是数据对齐的基准线——它帮你判断哪些变更该保留、哪些该丢弃。
先停写,再比对备份与两个主库的 GTID 集合
脑裂本质是两个节点都自认为主,各自接受写入,导致 binlog event 不一致。此时最关键是停止所有写流量(cluster.fenceWrites() 或手动设 super_read_only=ON),然后立刻获取三者的 GTID 执行集:
- 从最近一次全量备份中解析出备份时刻的
gtid_executed(可用mysqlbinlog --base64-output=DECODE-ROWS -v查看备份 SQL 文件末尾的 SET 命令) - 分别连接两个疑似主库,执行
SELECT @@global.gtid_executed; - 若任一节点的 GTID 集合不包含备份点的 GTID,则说明该节点在备份后丢失过部分事务,优先排除
注意:GTID 对齐不是看谁“数字大”,而是看是否能构成连续子集。如果两节点都包含备份 GTID,但彼此互不包含对方新事务,说明冲突已实际发生,必须人工介入比对。
用备份 + binlog 回放还原“可信主库”到脑裂前状态
选定一个你认为更可靠的主库(比如日志时间戳更新、应用写入路径更集中、或监控显示其未经历网络抖动),把它作为基准主库,用备份+binlog 将它回退到脑裂发生前的精确状态:
- 用
mysqldump备份文件恢复出一个临时实例(不要覆盖线上) - 用
mysqlbinlog --include-gtids='xxx' --exclude-gtids='yyy'提取从备份点到脑裂开始时间之间的 binlog,只重放可信主库上独有的事务 - 跳过另一主库写入的 GTID 区间(需提前记录脑裂窗口,例如通过应用日志或哨兵切换时间戳定位)
- 验证临时库的业务关键表数据量、校验和是否与脑裂前监控一致
这一步不是为了“恢复”,而是为了构造一个干净、可验证的参考状态,用于后续冲突行比对。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
逐表比对冲突数据,手工合并或标记异常
脑裂后真正难处理的是那些被两个主库同时修改过的同一行(例如相同 id 的订单状态)。自动合并几乎不可能,必须结合业务语义:
- 导出两主库中所有疑似冲突表的
id和updated_at(或业务时间戳字段),找出交集 - 对每个冲突
id,拉取两库的完整行(含gtid_executed上下文),比对字段变更逻辑 - 例如:A 库把订单状态从
pending改为paid,B 库改为cancelled,则需查支付网关回调日志确认真实状态 - 无法判定时,把该行写入
conflict_log表并暂停同步,等业务方确认
别指望 pt-table-sync 或类似工具全自动解决——它们只能同步无冲突的行,对脑裂场景下的语义冲突无能为力。
重建集群时禁用自动故障转移,直到人工确认完成
即使你完成了数据对齐,只要集群配置里还开着 auto-failover 或哨兵的 quorum 机制,就可能再次触发误判。必须显式干预:
- 在 MySQL Shell 中对 ClusterSet 执行
cluster.fenceAllTraffic(),彻底隔离旧集群 - 用
dba.rebootClusterFromCompleteOutage()重建新集群,且不启用multiPrimary模式 - 所有客户端连接串强制指向新主库,删掉旧主库的 DNS 记录或 VIP
- 至少保留 72 小时旧主库只读实例,供追溯审计,之后再清理
最容易被忽略的一点:脑裂恢复后,gtid_purged 往往已失真,后续做从库扩容或搭建新副本时,必须用物理备份或 mysqldump --set-gtid-purged=OFF 导出,否则复制会因 GTID 缺失直接中断。

















