主库异常宕机导致gtid_executed与mysql.gtid_executed不一致:因GTID仅在binlog切换时落盘,宕机使内存中GTID未写入表,重启后初始化仅读表+扫描最后一个binlog,若其缺失GTID EVENT或Previous-GTIDs且binlog_gtid_simple_recovery=ON,则更早binlog中的GTID被跳过,形成空洞;此时备库Executed_Gtid_Set超出主库gtid_executed,复制中断,须补齐主库GTID连续性而非跳过。

主库宕机导致 gtid_executed 与 mysql.gtid_executed 不一致
MySQL 5.7+ 的 GTID 持久化依赖两个地方:gtid_executed 变量(内存)和 mysql.gtid_executed 表(磁盘)。主库正常运行时,每个事务提交都会更新内存变量;但只有在 binlog 切换(rotate)时,才会把当前已执行的 GTID 集合刷入 mysql.gtid_executed 表。一旦主库异常宕机(如 kill -9、断电),最后一批事务的 GTID 可能只写进了内存、没来得及落盘到表中——重启后,MySQL 初始化 gtid_executed 时只读取该表 + 扫描最后一个 binlog 中的 GTID 事件,而这个 binlog 若未完整写入 Previous-GTIDs 或缺失 GTID EVENT,就会漏掉这部分 GTID,形成空洞。
binlog_gtid_simple_recovery=ON 加剧空洞风险
这个参数默认开启,作用是加速启动时 GTID 初始化:MySQL 会从最后一个 binlog 开始反向扫描,一旦遇到一个不含 GTID EVENT 也不含 Previous-GTIDs 的 binlog,就直接停止扫描,不再往前读更早的 binlog。如果宕机前刚好写了一个“空” binlog(比如刚 rotate 出来但还没写任何事务),且它不满足扫描条件,那么更早 binlog 中已执行但未持久化的 GTID 就彻底被跳过。
- 典型现象:主库重启后
SELECT @@global.gtid_executed明显小于宕机前值,且mysql.gtid_executed表里没有对应记录 - 验证方式:
SHOW BINARY LOGS查看 binlog 文件列表,再用mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.0000xx检查末尾几个文件是否含 GTID EVENT 或 Previous-GTIDs - 临时规避:启动前设
binlog_gtid_simple_recovery=OFF,强制全量扫描所有 binlog(但启动变慢)
备库报错 Slave has more GTIDs than the master has 的根源
这不是备库“多”了,而是主库“少”了——备库还在按原轨迹执行,主库重启后 GTID 范围收缩,导致备库的 Executed_Gtid_Set 超出主库当前 gtid_executed。此时 IO 线程拒绝拉取新事件,报错提示“server_uuid 匹配但 GTID 集合不包含备库已有部分”。
- 不能靠
SET GTID_NEXT注入空事务修复,因为问题在主库端,不是事务冲突 - 也不能用
CHANGE MASTER TO ... MASTER_AUTO_POSITION=1直接重连,GTID 自动定位会失败 - 必须先让主库恢复完整 GTID 视图:要么从备份恢复
mysql.gtid_executed表,要么人工补全缺失的 GTID(需确认 binlog 文件尚存且可解析)
真正容易被忽略的点:空洞 ≠ 数据丢失,但会阻断 GTID 复制链路
GTID 空洞本身不意味着数据丢失(只要 binlog 文件还在,事务日志就还在),但它会让基于 MASTER_AUTO_POSITION=1 的复制无法继续——因为 MySQL 要求主库的 gtid_executed 必须覆盖从库已执行的全部 GTID。修复的关键不是“跳过”,而是“补齐”主库视角下的 GTID 连续性。如果 binlog 文件损坏或被 purge,空洞就不可逆,只能降级为基于 position 的复制或重建从库。


















