Internal XA的本质是将binlog作为协调者解决binlog与redo log状态脱节问题:prepare阶段仅刷redo log并标记PREPARED,commit阶段以binlog写成功为最终判决依据,崩溃恢复时通过XID匹配binlog决定提交或回滚。

Internal XA 的本质是 binlog 作为协调者
MySQL 内部 XA 不是为分布式事务设计的,而是为解决 binlog 和 redo log 两套日志系统之间的状态脱节问题。Server 层(binlog)和 InnoDB 层(redo log)各自维护事务状态,但彼此不感知——binlog 不知道 InnoDB 是否真正提交,redo log 也不知道 binlog 是否写成功。Internal XA 把 binlog 当作事务协调者(TM),InnoDB 当作唯一资源管理器(RM),强制走两阶段:Prepare 阶段由 InnoDB 控制,Commit 阶段由 binlog 写入结果驱动。
prepare 阶段只刷 redo log,不碰 binlog
事务执行 COMMIT 时,InnoDB 先完成以下动作:
- 将修改写入
redo log buffer,并根据innodb_flush_log_at_trx_commit=1强制fsync()到磁盘 - 在
redo log中写入XID并标记事务为PREPARED状态(不是COMMITTED) - 不操作
binlog,也不释放行锁、间隙锁等资源
此时事务处于“半提交”状态:数据可恢复,但对外不可见,也未进入复制流。
commit 阶段以 binlog 写成功为最终判决依据
Server 层收到 InnoDB 的 prepare 成功响应后,才开始第二阶段:
- 将事务的完整变更(含
XID_EVENT)写入binlog缓冲区,并按sync_binlog设置决定是否fsync() - 仅当
binlog写盘成功,Server 层才通知 InnoDB 执行commit(即在redo log中写COMMIT标记、释放锁、清理回滚段) - 如果
binlog写失败(如磁盘满、权限不足),Server 层直接向 InnoDB 发送rollback请求
关键点:binlog 的写入成功与否,才是事务是否成功的最终判据;redo log 的 COMMIT 标记只是执行结果,不是前提条件。
崩溃恢复时靠 XID 匹配决定提交还是回滚
MySQL 重启后,恢复流程不是简单重放 redo log,而是交叉验证:
- 扫描所有
redo log,找出状态为PREPARED的事务及其XID - 读取最新
binlog文件,提取所有XID_EVENT中的XID - 对每个
PREPARED事务的XID,检查是否存在于binlog中: - 存在 → 补写
redo log的COMMIT标记,提交事务 - 不存在 → 回滚该事务(因为
binlog没写成,主从无法同步,必须丢弃)
这个机制决定了:哪怕 redo log 已落盘、binlog 却卡在缓冲区或写一半就 crash,恢复时也会被干净回滚——一致性保障的临界点,始终落在 binlog 是否完整。
真正容易被忽略的是 sync_binlog 和 innodb_flush_log_at_trx_commit 的组合效果:两者都设为 1 才能保证强一致性,但性能代价明显;若其中任一设为 0,崩溃时就可能丢失已提交事务的 binlog 记录,导致主从不一致或 PITR 失效。这不是 bug,而是配置权衡下的明确行为边界。


















