GTID是跨机房零数据丢失迁移的唯一可行基础,因其支持事务级唯一标识、自动定位与断点续传;传统位点复制在高延迟、时钟不同步等场景下必然失步或报错。

GTID 是跨机房零数据丢失迁移的唯一可行基础,不启用 GTID 的传统位点复制(MASTER_LOG_FILE/MASTER_LOG_POS)在高延迟、时钟不同步、binlog 清理策略不一致的跨机房环境下必然失步或报错。
为什么必须先设 ENFORCE_GTID_CONSISTENCY=WARN
这是整个迁移最易被跳过的“预警探针”。直接设为 ON 会导致大量合法 SQL 突然失败,比如含 CREATE TEMPORARY TABLE 的存储过程、NOW() 出现在语句中但未启用 binlog_format=ROW、MyISAM 表参与事务等。
该模式下 MySQL 仅在错误日志中记录 GTID consistency violation,不阻断执行;必须盯住 /data/mysql/error.log,持续 3–5 天,确认无此类警告才可推进。
- 常见触发点:
CREATE TABLE SELECT、函数如NOW()或RAND()在STATEMENT格式下使用 - 必须同步检查所有业务 SQL 和定时任务脚本,不能只看主库——从库也可能执行不兼容语句
- 若发现警告,需改写 SQL 或临时禁用相关功能,而非强行切 GTID
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 GTID_MODE=OFF_PERMISSIVE:允许新事务仍用匿名 ID,老事务继续存在 - 再执行
SET GLOBAL GTID_MODE=ON_PERMISSIVE:新事务开始生成 GTID,但旧匿名事务仍可复制 - 必须等
SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT'返回0后,才能执行最终的SET GLOBAL GTID_MODE=ON - 这一步卡住,说明还有未同步完的匿名事务(比如某从库延迟严重),强行切会导致从库
SQL_THREAD报错Could not execute Write_rows event on table
CHANGE MASTER TO MASTER_AUTO_POSITION=1 不等于“自动成功”
很多人以为只要执行了这条命令就万事大吉,其实它只表示“启用 GTID 自动匹配”,真正能否连上、是否跳过冲突事务,取决于三个隐性条件:
-
log_slave_updates=1必须开启:否则从库自己产生的 binlog 没有 GTID,级联复制断裂 -
binlog_format必须为ROW:STATEMENT格式在跨机房高延迟下极易因非确定函数(RAND(),UUID())导致主从数据不一致 -
slave_net_timeout=60需显式调大:默认 60 秒太短,跨机房网络抖动容易触发 IO 线程重连,造成复制中断
执行后务必验证:SHOW SLAVE STATUS\G 中 Retrieved_Gtid_Set 和 Executed_Gtid_Set 是否持续增长且差值稳定缩小。
切换前必须验证的 4 个状态点
Seconds_Behind_Master 显示为 0 不代表真正追平——并行复制下它可能“假归零”。必须逐项验证:
-
Slave_IO_Running和Slave_SQL_Running都为Yes(仅看状态,不看延迟) -
Seconds_Behind_Master稳定为 0 至少 60 秒(避免瞬时追平后又积压) - 主库执行
SELECT @@global.gtid_executed记录当前 GTID 集合,从库执行SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('xxx', 10)等待该集合执行完毕,返回0才可靠 - 主库执行
FLUSH TABLES WITH READ LOCK后立刻查从库同表的COUNT(*),确认无差异(锁只持几秒,用于最终一致性快照)
最后一步最容易被忽略:应用连接池缓存旧连接、DNS 缓存未刷新、/etc/hosts 未清理,都会让请求继续打到旧主——哪怕数据库层已切完。


















