综合 binlogless 从库 + 并行复制:复制的 FLUSH PRIVILEGES 在 mysql.gtid_executed 里留空洞,重启后复制中断

2026-10-20 21:09:00

binlogless 从库 + 并行复制:复制的 FLUSH PRIVILEGES 在 mysql.gtid_executed 里留空洞,重启后复制中断

信源与结论

官方 release note 结论:MySQL 9.7.4(2026-10-20)Replication Notes:

On a binlogless replica using relay-log recovery, parallel replication, and commit-order preservation, replicated FLUSH PRIVILEGES statements could leave gaps in mysql.gtid_executed. After restart, the replica could request already-applied transactions, resulting in duplicate errors or failure when the source had purged the required binary logs.

报错文本形如:

Cannot replicate because the source purged required binary logs.
Replicate the missing transactions from elsewhere, or provision a new replica from backup

Bug #115613, Bug #36849620。Release notes:

触发条件需要同时满足四项:从库 log_bin=OFF(binlogless)、使用 relay-log recovery、开启并行复制、开启 commit-order preservation。缺任何一项,都没落在该 bug 描述的场景里。

故障链拆解

推断(需自行验证),以下是参数语义层面的解释,不是 release note 原文:

  1. gtid_executed 记录已执行事务的 GTID 集合。当 log_bin=OFF 且 log_replica_updates=OFF 时,从库不把应用过的事务写进自己的 binlog,gtid_executed 的维护就落到 mysql.gtid_executed 表上(默认每 1000 个事务刷一次,gtid_executed_compression_period 控制压缩)。
  2. 并行复制 + commit-order preservation(replica_preserve_commit_order=ON,依赖 log_replica_updates 或 binlog)下,事务按主库顺序提交,但并行 worker 各自应用的先后与 GTID 集合更新并不是同一把锁。FLUSH PRIVILEGES 这类管理语句走特殊处理路径,可能绕过常规的 GTID 写入路径,于是表里出现空洞。
  3. 空洞的后果:重启后从库按 gtid_executed 与 relay log 当前位点比对,发现主库上「某些 GTID 自己没记为已执行」,于是重新请求这些事务。如果主库已经把对应 binlog 清掉(binlog_expire_logs_seconds 到期),复制直接报错中止。
  4. Cannot replicate because the source purged required binary logs 是 ER_SOURCE_FATAL_ERROR_READING_BINLOG / 相关错误路径的典型提示。

识别:先看参数和日志

SELECT @@global.log_bin, @@global.log_replica_updates, @@global.gtid_mode;
SELECT @@global.replica_preserve_commit_order, @@global.replica_parallel_workers;
SELECT COUNT(*) FROM mysql.gtid_executed;
SELECT @@global.gtid_executed;
-- 比对主库与从库 GTID 集合差异(用 gtid_subset / 逐段比较)
-- 8.0+ 可用:
SELECT GTID_SUBTRACT(@@global.gtid_executed /*source*/, (SELECT ...))  -- 需按实际拼装

错误日志里搜 ER_MASTER_FATAL_ERROR_READING_BINLOG / Cannot replicate because the source purged。日志里同时会打出缺失的 GTID 段,可以直接和主库的 gtid_executed、mysql.gtid_executed 对齐看差在哪一段。

复现思路

推断(需自行验证):按上面四个条件搭一套环境——从库 log_bin=OFF、log_replica_updates=OFF、replica_parallel_workers>1、replica_preserve_commit_order=ON;主库执行 FLUSH PRIVILEGES 并让对应 GTID 应用到从库;然后在从库核对 mysql.gtid_executed 里是否落了这条 GTID。确认有空洞后重启从库 SQL 线程,并把主库 binlog 清到该 GTID 之前,即可复现报错。日常环境不必特意构造,巡检时直接查 GTID 集合差异更快。

规避与修复

  1. 升级到 9.7.4(含修复)。旧版本仍在跑的,升级窗口内不要把主库 binlog 保留期设得太短。
  2. 主库 binlog_expire_logs_seconds 留出足够窗口:至少覆盖一次全量备份周期 + 从库最长滞后时间。
  3. 从库上开 log_replica_updates=ON 并保留 binlog(磁盘允许的话),让 gtid_executed 走 binlog 路径,减少对 mysql.gtid_executed 表的依赖。
  4. 定期比对主从 GTID 集合,发现从库缺 GTID 立刻处理(重新拉取 / 重建),不要等到主库 purge 之后才发现。
  5. 已经报这个错之后的恢复:从备份重建从库通常比手工补事务更稳;也可以从其他从库 / 延迟从库上补,但必须先确认那边的 GTID 集合是完整的。
  6. FLUSH PRIVILEGES 这类语句常出现在业务代码里(不少框架的权限刷新逻辑会调用),它不是业务 DML,容易被漏掉,排查复制问题时别只盯着表和 DML。
复制全文 生成海报 MySQL 复制 运维 GTID

推荐文章

程序员茄子在线接单