mysql集群升级时主从数据不一致必然发生,关键在于版本差异、配置未对齐或顺序错误引发gtid跳变、binlog_format兼容性断裂及sql_mode差异等导致sql线程失败;必须严格按先从后主顺序升级,并校验binlog_format、sql_mode、max_allowed_packet、autocommit与innodb_flush_log_at_trx_commit、server_id与gtid_mode五项配置,升级后须执行gtid子集比对和pt-table-checksum全量校验。

MySQL集群升级时,主从数据不一致不是“会不会发生”的问题,而是“在哪一步、因什么触发”的问题。只要主从版本不同、配置未对齐、或升级顺序错乱,GTID跳变、binlog_format兼容性断裂、甚至sql_mode差异都可能让SQL线程在从库执行失败——此时Slave_SQL_Running直接变No,数据开始脱节。
升级前必须校验的5个关键配置项
很多团队只关注版本号,却忽略底层行为一致性。以下配置不统一,哪怕同是8.0.x,也可能导致复制中断:
-
binlog_format:主库设为ROW,从库若仍是STATEMENT,某些函数(如UUID()、NOW())回放结果不同 -
sql_mode:主库启用STRICT_TRANS_TABLES,从库没开,会导致插入截断不报错,但主库已拒绝 -
max_allowed_packet:主库设为64M,从库仅4M,大事务(如批量导入)直接被SQL线程丢弃 -
autocommit和innodb_flush_log_at_trx_commit:影响binlog与redo写入顺序,主从落盘时机差1ms就可能造成GTID不连续 -
server_id和gtid_mode:升级后若从库gtid_mode=OFF而主库已开,CHANGE MASTER TO会报错ERROR 3021 (HY000)
主从升级顺序不能颠倒:先从后主
这是最容易踩的坑——有人觉得“主库最重要,得先升”,结果升完主库,从库立刻同步失败。原因很简单:新主库生成的binlog事件(比如新增的DDL语法、优化器hint)旧从库压根不认识。
正确顺序只有一条:
- 停所有写流量(或切到维护页),确保主库无新事务写入
- 逐台升级从库(建议滚动升级,避免全量读不可用),每台升级后执行:
STOP SLAVE; START SLAVE;,并确认Seconds_Behind_Master = 0且无Last_SQL_Errno - 最后升级主库,升级完成立即检查
SHOW MASTER STATUS,确认File和Position未重置;再用CHANGE MASTER TO MASTER_AUTO_POSITION = 1重新拉起从库复制(如果启用了GTID)
注意:RESET MASTER绝对不能在升级主库后执行——它会清空binlog索引,从库再也找不到同步起点。
升级后必做:GTID一致性校验与事务补漏
即使升级过程顺利,也不能默认数据就一致。GTID模式下,SELECT @@GLOBAL.GTID_EXECUTED返回的是已执行事务集合,但不保证每个事务在主从上执行结果完全相同(比如主库某条UPDATE因索引缺失走全表扫描,从库有索引走range,更新行数不同)。
所以升级后第一件事不是放流量,而是:
- 在主库执行:
SELECT GTID_SUBSET(@@GLOBAL.GTID_EXECUTED, (SELECT @@GLOBAL.GTID_EXECUTED FROM slave_host))—— 若返回0,说明从库缺失GTID,需人工定位缺失事务 - 用
pt-table-checksum跑全量校验(注意加--replicate参数存结果到专用表),重点看Differences列非0的表 - 对差异表,用
pt-table-sync --sync-to-master生成修复SQL,**务必先在测试库回放验证**,再上生产
别省这一步——线上见过太多“升级完一切正常”,结果三天后业务查出订单金额对不上,根源是某张日志表的TIMESTAMP字段在新旧版本下默认值处理逻辑变了。
真正危险的不是升级动作本身,而是升级后那段“看起来正常”的静默期。GTID不报错、Seconds_Behind_Master为0、SHOW SLAVE STATUS全绿,不代表每一行数据都对得上。校验必须落到具体表、具体字段、具体事务ID,否则所谓“一致”只是幻觉。











