xa事务在slave上回放失败的直接原因是mysql 5.7+中xa prepare被写入binlog,但后续commit/rollback可能未落盘或未同步,导致slave仅收到prepare而无对应commit,触发1399或1410错误;gtid模式下若出现anonymous_gtid(如主库锁超时、崩溃或sql_log_bin=0导致gtid未生成),则因auto_position=1强制要求gtid而拒绝执行;binlog截断时mysql自动注入无xid上下文的xa rollback,反而引发复制中断;跳过事务需严格区分模式:非gtid下sql_slave_skip_counter可能跳过prepare却使commit失序,gtid下必须精准提取失败事务gtid并注入空事务,且若事务已prepared还需先xa recover再xa rollback清理状态,否则复制重启仍失败。

XA事务在Slave上回放失败的直接原因
根本问题在于:MySQL 5.7+ 的 XA PREPARE 会被写入 binlog,但后续 COMMIT 或 ROLLBACK 可能因各种原因未成功落盘或未同步,导致 Slave 收到不完整的 XA 事务事件流。Slave SQL 线程尝试回放时,发现只有 XA PREPARE 没有对应 XA COMMIT,就会报错中断,典型错误码是 1399(ER_XAER_NOTA)或 1410(ER_XA_RBTIMEOUT)。
GTID_MODE=ON 时出现 Anonymous_GTID 的陷阱
当 Slave 报错 Cannot replicate anonymous transaction when AUTO_POSITION = 1,说明它收到了一个没有 GTID 的 XA 事务——即 Anonymous_GTID 事件。这通常发生在主库或中间从库(如 db-4)因锁等待超时、崩溃或 sql_log_bin=0 下手动 XA COMMIT,导致事务在 binlog 中丢失 GTID 分配逻辑。关键点是:thd->owned_gtid.sidno > 0 不成立时,MySQL 就不会生成 GTID_log_event,而是 fallback 到匿名模式。而 GTID 复制(AUTO_POSITION=1)强制要求所有事务必须带 GTID,因此直接拒绝执行。
binlog 截断或 crash 后自动注入 ROLLBACK 的副作用
MySQL 在检测到 binlog 或 relay log 不完整时,会自动补一条 XA ROLLBACK 事件来“收尾”。但 XA 事务不能像普通事务那样被随意 rollback——它必须先 XA RECOVER 查出 xid,再显式 XA ROLLBACK xid。自动注入的 rollback 没有 xid 上下文,Slave 执行时就卡住,报错 Coordinator stopped because there were error(s) in the worker(s)。这种机制本意是容错,结果反而成了复制中断的导火索。
修复时最容易忽略的细节
跳过事务不是万能解法,尤其对 XA:
- 用
SET GLOBAL sql_slave_skip_counter = 1在非 GTID 模式下可能跳过 prepare 事件,但后续 commit 仍会找不到上下文,再次失败 - GTID 模式下必须用
SET GTID_NEXT='xxx'+ 空事务方式跳过,但前提是你要从performance_schema.replication_applier_status_by_worker或错误日志里准确提取出那个失败事务的完整 GTID 字符串(含 UUID 和 sequence number),漏一位都无效 - 如果事务已处于
XA PREPARED状态但未提交,仅跳过无法清理状态,需在 Slave 上先XA RECOVER查出 xid,再手动XA ROLLBACK xid,否则下次启动复制仍会重试该事务
XA 复制问题从来不是单点故障,而是 prepare/commit 落盘顺序、GTID 分配时机、crash 恢复策略三者耦合的结果。查日志时盯紧 mysql-bin.* 解析输出里的 Xid_log_event 和 GTID_log_event 是否成对出现,比盲目跳过更可靠。











