MySQL两阶段提交(2PC)具体执行流程是怎样的?
摘要:两阶段提交(2PC)是MySQL保证redo log(InnoDB层) 和binlog(Server层) 一致性的核心机制,其执行流程严格分为「Prepare阶段」和「Commit阶段」,每个阶段都有明确的核心动作、数据状态变化和异常处理逻
两阶段提交(2PC)是MySQL保证redo log(InnoDB层) 和binlog(Server层) 一致性的核心机制,其执行流程严格分为「Prepare阶段」和「Commit阶段」,每个阶段都有明确的核心动作、数据状态变化和异常处理逻辑。本文会结合InnoDB底层逻辑,拆解2PC从触发到完成的每一步,附实战案例和状态流转图,让你彻底掌握执行细节。
一、前置背景:2PC的触发场景
2PC仅在显式/隐式事务提交时触发(执行COMMIT语句,或autocommit=1时单条DML执行完成),且仅针对写事务(INSERT/UPDATE/DELETE)——读事务(SELECT)无redo log/binlog写入,无需2PC。
以下面的更新事务为例,全程拆解2PC流程:
-- 测试表(主键索引)
CREATE TABLE `user` (
`id` int PRIMARY KEY,
`name` varchar(20)
) ENGINE=InnoDB;
-- 显式事务(触发2PC)
BEGIN;
UPDATE `user` SET `name` = '张三' WHERE `id` = 1;
COMMIT; -- 触发2PC流程
二、2PC完整执行流程(分2大阶段+7个步骤)
整体流转框架
graph TD
A[客户端执行COMMIT] --> B[MySQL Server接收提交请求]
B --> C[Prepare阶段]
C --> C1[InnoDB刷redo log到磁盘,标记prepare]
C1 --> C2[Server层记录prepare状态]
C2 --> D{Prepare是否成功?}
D -->|失败| E[回滚事务,返回客户端失败]
D -->|成功| F[Commit阶段]
F --> F1[Server层刷binlog到磁盘]
F1 --> F2[InnoDB标记redo log为commit]
F2 --> F3[InnoDB释放行锁/表锁]
F3 --> F4[更新事务状态为已提交]
F4 --> G[返回客户端提交成功]
阶段1:Prepare阶段(核心:刷redo log,标记预备提交)
Prepare阶段是2PC的“预提交”环节,核心目标是将redo log持久化到磁盘,并标记事务状态为“待确认提交”,为后续崩溃恢复做准备。
步骤1:MySQL Server层接收Commit请求
当客户端执行COMMIT时,MySQL Server层(非InnoDB引擎)首先接收到提交指令,暂停当前事务的其他操作,进入2PC流程。
步骤2:Server层向InnoDB引擎发起Prepare请求
Server层调用InnoDB的事务提交接口,传递当前事务ID(trx_id),通知InnoDB执行Prepare操作。
步骤3:InnoDB刷盘redo log(核心动作)
InnoDB执行以下关键操作:
整理redo log buffer:将当前事务所有的redo log记录(如“id=1的数据页,name字段从‘李四’改为‘张三’”)从内存缓冲区(redo log buffer)中整理出来;
强制刷入磁盘:根据innodb_flush_log_at_trx_commit=1(生产环境必配),将redo log从内存刷入磁盘的redo log文件(ib_logfile0/ib_logfile1);
刷盘规则:调用操作系统的fsync()函数,保证日志真正写入磁盘(而非操作系统缓存);
标记redo log的Prepare状态:在redo log中写入一条“事务trx_id=XXX,状态=PREPARE”的记录,关联当前事务的所有修改。
关键:此时redo log已持久化,但事务并未“真正提交”——其他事务通过MVCC读取时,仍看不到该事务的修改(Read View未更新)。
步骤4:InnoDB向Server层返回Prepare结果
InnoDB完成redo log刷盘后,向Server层返回“Prepare成功”的响应;若刷盘失败(如磁盘满、IO错误),则返回“Prepare失败”。
阶段2:Commit阶段(核心:刷binlog,确认最终提交)
Commit阶段是2PC的“最终提交”环节,核心目标是将binlog持久化到磁盘,并通知InnoDB标记事务为“已提交”,完成整个事务流程。
