确认gtid集合真有空洞需查show slave status\g中的retrieved_gtid_set与executed_gtid_set差集,空洞指已拉取但未执行的gtid;同一source_id内transaction_id不连续才算空洞,跨source_id不可比大小。

GTID集合出现空洞,不能靠“跳过”解决,必须先定位空洞来源,再决定是注入空事务补全,还是重建从库。
怎么确认 GTID 集合真有空洞?
空洞不是看 Executed_Gtid_Set 里数字不连续就认定的——GTID 是按 source_id 分组的,同一 source_id 内 transaction_id 连续才叫“无空洞”。常见误判点:
- 把不同 source_id 的事务 ID 拼起来比大小,比如看到
uuid1:1-5, uuid2:10-15就以为中间缺了6-9,其实完全无关 - 忽略
Retrieved_Gtid_Set和Executed_Gtid_Set的差值:真正空洞 =Retrieved_Gtid_Set - Executed_Gtid_Set,这个差集才是从库“收到了但没执行”的事务 - 用
SHOW MASTER STATUS的Executed_Gtid_Set直接跟从库比——主库可能 purge 过旧 GTID,已不可比
正确做法是登录从库执行:
SHOW SLAVE STATUS\G,重点核对三处:
-
Retrieved_Gtid_Set:IO 线程已拉到的全部 GTID(含 relay log 中未执行部分) -
Executed_Gtid_Set:SQL 线程实际执行过的 GTID -
Last_SQL_Error:如果报错,里面通常带出卡住的那个 GTID,比如gtid:abc123:456
GTID 空洞的两种典型成因与对应动作
空洞 ≠ 错误,它只是状态描述。是否需要干预,取决于空洞怎么来的:
-
人为关闭
sql_log_bin导致的空洞:主库执行了SET sql_log_bin = OFF后的 DML,不会生成 GTID,也不会同步到从库。这种空洞无法补全,只能人工在从库补相同数据,或回滚主库操作。查法:SELECT @@sql_log_bin在主库会话中确认;查 binlog:mysqlbinlog --base64-output=DECODE-ROWS -v看对应时间点有没有缺失事件 -
relay log 损坏或 SQL 线程崩溃导致的空洞:IO 线程已拉取并写入 relay log,但 SQL 线程在回放中途挂掉,且没来得及更新
Executed_Gtid_Set。此时可安全注入空事务补全,前提是能准确定位那个“已收到、未执行”的 GTID
注入空事务补全 GTID 空洞的实操要点
这一步本质是“让从库假装执行过那个 GTID”,不是跳过下一条。漏一步或抄错字符,Executed_Gtid_Set 就永久错乱,后续同步必然失败:
- 必须先
STOP SLAVE(MySQL 8.0.22+ 推荐用STOP REPLICA) -
SET GTID_NEXT = 'xxx:nnn'中的值,必须严格来自Last_SQL_Error或通过mysqlbinlog解析Relay_Master_Log_File+Exec_Master_Log_Pos定位到的 exact GTID,冒号前后不能有空格,大小写敏感 - 空事务必须是
BEGIN; COMMIT;,不能有任何 DML,不能断开连接,不能在事务里做其他操作 - 执行完
COMMIT后,立刻SET GTID_NEXT = 'AUTOMATIC',再START SLAVE - 验证:检查
Executed_Gtid_Set是否已包含刚注入的 GTID,且Seconds_Behind_Master开始下降
什么时候该放弃补全,直接重建从库?
补全只适用于单点、明确、孤立的 GTID 空洞。以下情况别硬撑:
-
Retrieved_Gtid_Set和Executed_Gtid_Set差集超过 10 个 GTID,尤其跨多个 source_id - 从库
relay_log文件损坏、缺失,或Relay_Master_Log_File指向一个不存在的 binlog 文件 - 主库已执行
PURGE BINARY LOGS,且被 purge 的 binlog 正好覆盖了空洞对应的事务 - 你不确定空洞是哪条语句造成的,也没法还原当时主库的操作上下文
这时候最省事也最安全的做法,是用 mysqldump --set-gtid-purged=ON 重新导出主库,再导入从库并重配复制。补全 GTID 空洞不是技术炫技,而是为了快速恢复同步;当路径变长、风险变高时,重建就是更短的路。











