必须用gtid,否则跨机房迁移90%以上会因位点丢失或重复执行失败;gtid_mode须严格四步切换(off→off_permissive→on_permissive→on),且需先经warn模式验证兼容性、确保log_slave_updates=1、binlog_format=row、gtid_purged集合兼容。

必须用 GTID,否则跨机房迁移链路重构就是埋雷。传统基于 MASTER_LOG_FILE 和 MASTER_LOG_POS 的切换,在主从延迟高、binlog 清理策略不一致、时钟不同步的场景下,90% 以上会卡在位点找不到或重复执行上。
为什么不能直接 CHANGE MASTER TO MASTER_AUTO_POSITION=1
很多人以为只要执行这句就自动接上了,实际它只是“声明启用 GTID 匹配”,真正能否连通取决于三个隐性前提是否满足:
-
log_slave_updates=1必须开启——否则从库自己产生的 binlog 没有 GTID,级联复制断裂,下游无法继续同步 -
binlog_format必须为ROW——STATEMENT或MIXED在跨机房高延迟下,因NOW()、RAND()、UUID()等非确定函数极易导致主从数据不一致 - 主库与目标从库的
gtid_purged集合必须兼容——若目标从库缺失主库已产生的一部分 GTID(比如它之前清过 binlog),START SLAVE会直接报错Client requested master to start replication from position after the end of binary log
GTID_MODE 切换必须分四步,跳步必报错
MySQL 5.7+ 强制要求 gtid_mode 只能按顺序过渡:OFF → OFF_PERMISSIVE → ON_PERMISSIVE → ON。跳步会触发 ERROR 1789 (HY000): Cannot change gtid_mode while @@global.gtid_mode is OFF_PERMISSIVE。
- 先设
SET GLOBAL enforce_gtid_consistency=WARN,盯住/data/mysql/error.log至少 3 天,确认无GTID consistency violation警告再推进 -
SET GLOBAL GTID_MODE=OFF_PERMISSIVE:允许新事务仍用匿名 ID,老事务继续存在 -
SET GLOBAL GTID_MODE=ON_PERMISSIVE:新事务开始生成 GTID,但旧匿名事务仍可复制;此时需等SELECT @@GLOBAL.GTID_OWNED返回空,说明无活跃匿名事务 - 最后执行
SET GLOBAL GTID_MODE=ON;若卡住,说明某从库仍有未同步完的匿名事务(常见于延迟严重节点),需先追平再切
重构链路时如何避免从库跳过关键事务
切换复制源后,START SLAVE 不等于“自动重放所有缺失事务”。GTID 复制靠的是 Executed_Gtid_Set 和 Retrieved_Gtid_Set 的差集,但若目标从库的 gtid_purged 已包含部分主库 GTID,它会直接跳过——这不是 bug,是设计行为。
- 执行前务必比对:
SELECT @@global.gtid_purged;在主库和目标从库上分别查,确保从库的集合是主库的子集 - 若从库 purged 集合过大(比如曾手动导入过备份),需用
RESET MASTER清空并重新导入全量数据,否则无法安全接入 - 切换后立刻验证:
SHOW SLAVE STATUS\G中检查Seconds_Behind_Master是否持续下降,且Retrieved_Gtid_Set与主库Executed_Gtid_Set的交集在扩大 - 不要依赖
SELECT MASTER_POS_WAIT()——它只适用于传统模式;GTID 下应改用SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('uuid:1-100')
最易被忽略的一点:enforce_gtid_consistency=WARN 阶段的日志必须人工盯守,不是跑个脚本扫一遍就完事。很多线上事故都源于跳过这一步,结果在 ON 后突然爆出大量 CREATE TEMPORARY TABLE 报错,业务写入直接中断。











