mysql 8.0不能直接set global gtid_mode=on,因其状态机强制要求按off→off_permissive→on_permissive→on顺序切换,跳步会报error 1789;必须先设enforce_gtid_consistency=warn观察日志确认无gtid不兼容语句,再逐级切换,并确保主从ongoing_anonymous_transaction_count=0、gtid_purged与gtid_executed一致后方可设on。

为什么不能直接 SET GLOBAL GTID_MODE=ON
MySQL 8.0 强制要求 gtid_mode 必须按顺序切换:只能从 OFF → OFF_PERMISSIVE → ON_PERMISSIVE → ON,跳步会报错 ERROR 1789 (HY000): Cannot change gtid_mode while @@global.gtid_mode is OFF_PERMISSIVE。直接设为 ON 不仅失败,还会中断复制流程。
如何安全清空匿名事务残留
关键不是“切模式”,而是等所有匿名事务真正执行完、不再参与复制。必须在主从库上都确认:
-
SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT';返回值为0 -
SELECT * FROM performance_schema.replication_applier_status_by_coordinator;中LAST_PROCESSED_TRANSACTION字段不含ANONYMOUS字样 - 检查
SHOW SLAVE STATUS\G的Retrieved_Gtid_Set和Executed_Gtid_Set是否已收敛(尤其注意从库是否还卡在某个匿名事务的 relay log 里)
如果某从库延迟高,ONGOING_ANONYMOUS_TRANSACTION_COUNT 长期不归零,说明它还没把主库旧的匿名 binlog 拉完或重放完——此时强行切到 ON,SQL_THREAD 会立即报 Could not execute Write_rows event on table。
enforce_gtid_consistency=WARN 是唯一靠谱的兼容性探针
上线前必须先设 SET GLOBAL enforce_gtid_consistency=WARN;,持续观察 /data/mysql/error.log 至少 3 天。常见触发警告的操作包括:
- 含
CREATE TEMPORARY TABLE的存储过程 -
INSERT ... SELECT涉及 MyISAM 表 -
NOW()、RAND()、UUID()等非确定函数在STATEMENT格式下被记录 - 跨库更新(如
UPDATE db1.t1 JOIN db2.t2)但未显式加锁或事务隔离不一致
这些不是“语法错误”,而是 GTID 一致性模型拒绝接受的语义风险。WARN 模式下它们仍能执行,但日志里会有明确提示,这才是你调整业务 SQL 的真实依据。
切到 ON 后 MASTER_AUTO_POSITION=1 仍可能失败
很多人以为开了 GTID 就万事大吉,其实 CHANGE MASTER TO MASTER_AUTO_POSITION=1 成功只代表命令被接收,不代表复制真能跑起来。三个隐性条件缺一不可:
-
log_slave_updates=1必须开启(否则级联复制断裂,下游无法继续用 GTID) -
binlog_format=ROW必须生效(STATEMENT在跨机房高延迟下必然导致主从不一致) - 目标从库的
gtid_purged必须包含主库当前gtid_executed的全部前缀——若缺失,START SLAVE直接报Client requested missing transaction
最容易被忽略的是最后一点:用 SELECT @@global.gtid_purged; 对比主从,发现不一致时,必须用 SET GLOBAL gtid_purged = '...'; 手动补全(注意该操作要求 GTID_EXECUTED 为空,通常需先 RESET MASTER)。











