升级后gtid不一致通常由人为干预引发,需先区分是并行复制的临时空洞还是真实缺失;确认后按影响范围从小到大选择修复方式:单个gtid缺失用set gtid_next注入空事务,冗余gtid用set global gtid_purged裁剪,严重污染则reset slave all后重连。

升级后 GTID 不一致,通常不是“升级本身”导致的,而是升级过程中或前后人为干预(比如跳过错误、重置复制、误删 binlog)触发的。直接修复前先确认:这不是并行复制造成的临时空洞(Executed_Gtid_Set尾部不连续但末端连续),而是真实缺失或冗余。
怎么判断是真不一致还是假报警
在主库和从库分别执行:
SELECT @@GLOBAL.gtid_executed;
再查从库状态:
SHOW SLAVE STATUS\G
重点比对三处:
-
Retrieved_Gtid_Set:从库拉到但还没执行的 GTID 集合 —— 如果它比主库的gtid_executed少,说明网络或 IO 线程卡了,不是 GTID 逻辑问题 -
Executed_Gtid_Set:从库已执行的 GTID —— 如果它包含主库已PURGE的 GTID(比如uuid:1-50,而主库gtid_executed只剩uuid:51-),那就是真不一致 -
Auto_Position值是否为1:如果不是,说明复制没走 GTID 模式,后续所有 GTID 操作都无效
常见修复方式选哪个
别一上来就 RESET MASTER 或重搭从库。按影响范围从小到大排序:
- 如果只是单个 GTID 缺失(比如从库报错
Last_SQL_Error: Could not execute... GTID xxx:yyy),用SET GTID_NEXT注入空事务补上最轻量 - 如果从库多出几个 GTID(比如旧主重启后写入了本地事务),且能确认这些事务无关紧要,用
SET GLOBAL gtid_purged = 'xxx:1-y'手动裁剪掉多余部分(注意:该值必须是gtid_executed的子集,否则会报错) - 如果
gtid_purged已被污染(比如设成了错误范围),或缺失范围太大,RESET SLAVE ALL+ 重新CHANGE MASTER TO ... MASTER_AUTO_POSITION = 1是安全底线 —— 但它要求主库 binlog 还在,否则会报error 1236
为什么 SET GTID_NEXT 容易失败
很多人试了 SET GTID_NEXT = 'xxx:yyy'; BEGIN; COMMIT; 却发现 Executed_Gtid_Set 没变,或者报错 GTID_EXECUTED contains gap。原因有三个:
- 没在
STOP SLAVE状态下操作 —— 必须先停 SQL 线程,否则事务会被复制线程干扰 - 设的 GTID 已存在于
Executed_Gtid_Set中 —— MySQL 不允许重复执行,会直接忽略 - 设的 GTID 超出主库当前
gtid_executed范围(比如主库最大是xxx:100,你设xxx:105),MySQL 会拒绝并提示GTID is not in the executed set
升级后最常被忽略的配置点
MySQL 8.0+ 默认开启 enforce_gtid_consistency = ON,但升级前旧实例可能关着。升级后如果没检查,某些语句(比如 CREATE TEMPORARY TABLE)会直接报错,导致复制中断,进而引发 GTID 偏移。务必确认:
-
SELECT @@GLOBAL.enforce_gtid_consistency;返回ON -
SELECT @@GLOBAL.gtid_mode;返回ON(不是ON_PERMISSIVE) - 主库
my.cnf中没漏掉binlog_format = ROW—— STATEMENT 模式下 GTID 可能不生效
这些配置一旦不匹配,gtid_executed 就会和实际 binlog 内容对不上,后续所有修复动作都在补漏洞。











