MySQL 事务的二阶段提交是什么?
- 标签:后端、MySQL、数据库
🏛 业务架构师
二阶段提交指的是 redo log 与 binlog 的内部提交协调,不是业务层的 XA。流程:redo log 写入并置为 prepare 状态 → 写 binlog → redo log 置为 commit。
为什么要这么绕?因为两个日志必须同时成功或同时失败,否则出现不可解释的错账:
- 崩溃恢复时的规则:redo 处于 prepare,binlog 完整 → 提交;binlog 不完整 → 回滚。这样保证主库与 binlog(以及从库)对每笔事务的判定永远一致;
- 没有两阶段会怎样:假设先写 redo 后写 binlog,中间宕机——本库说"扣款成功",从库和基于 binlog 的下游(数仓、对账、搜索索引)说"没这笔交易",资损 + 对账永久对不平,这是支付系统最深的噩梦。
业务视角的引申:binlog 是数据资产的总线。主从复制、Canal 订阅同步缓存/ES、审计、数仓全部挂在它上面,所以 binlog 的完整性不只影响复制,还决定整条数据供应链的可信度。sync_binlog=1 与 innodb_flush_log_at_trx_commit=1 的"双 1"配置,就是用 fsync 成本买"这两个日志永不缺口"的保险——支付级业务必须双 1,日志类业务可以放宽,这就是我对不同业务定不同数据库参数档位的依据。
🏗 系统架构师
这里的两阶段提交是 redo log 与 binlog 之间的内部 XA,不是微服务那种分布式事务,目的是保证两个日志的逻辑一致,进而保证主从复制的正确性。
流程(以 update 为例):
- InnoDB 写 redo log,标记 prepare;
- Server 层写 binlog(含 redo 对应的 XID);
- InnoDB 将 redo 置为 commit。
崩溃恢复规则:
- redo 已 commit → 提交;
- redo 处于 prepare:查 binlog——binlog 完整(有对应 XID)则提交,不完整则回滚。
为什么必须两阶段:binlog 是所有存储引擎共享的、主从复制的根基。若先写 redo 后 crash 且没到 binlog——若以 redo 为准提交,主库有数据、binlog 缺失,从库永远追不上,主从不一致;两阶段让 binlog 成为最终裁决,恢复逻辑"以 binlog 是否完整为准",保证 binlog 与数据总是一致。参数 sync_binlog 与 innodb_flush_log_at_trx_commit 的组合(双 1)决定刷盘严格程度,是我做数据可靠性 vs 性能取舍的第一个旋钮。
💻 全栈开发专家
指的是 redo log 和 binlog 之间的一致性,MySQL 内部的两阶段提交:
text
① InnoDB 写 redo log,标记 prepare
② Server 层写 binlog 并刷盘
③ InnoDB 将 redo log 标记 commit为什么必须这样:两份日志独立落盘,中途任何时刻宕机都可能出现"redo 有 binlog 没有"(或反过来),导致主库和从库(靠 binlog 复制)数据不一致。
崩溃恢复规则(面试核心):
- redo 处于 prepare,binlog 完整 → 提交;
- redo 处于 prepare,binlog 不存在/不完整 → 回滚。
判断 binlog 完整性:事务结尾有 XID event,且整段 binlog 已刷盘。
工程配置:sync_binlog=1 + innodb_flush_log_at_trx_commit=1(双 1)保证两者都实时刷盘。曾经有系统为了性能设成 2/0,主机掉电后主从差了几十条订单,最后只能以 binlog 为准人工订正——所以核心交易库我永远双 1,性能靠别的地方补。