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标记事务为“已提交”,完成整个事务流程。
阅读全文