首页 >> 基础教程

mysql的两阶段提交

            MySQL 使用两阶段提交(2PC)机制(主要发生在存储引擎层如 InnoDB 和 Server 层的 Binlog 之间)是为了确保事务在涉及多个独立组件(尤其是 Binlog 和存储引擎的 Redo Log)时,仍然能严格满足 ACID 特性中的原子性(Atomicity)和持久性(Durability),特别是在发生系统崩溃等故障的情况下。


使用两阶段提交的核心原因在于 Binlog 和 Redo Log 是 MySQL 中两个独立且关键的部分,它们有不同的职责,但共同保障了数据的完整性和可恢复性

  1. Binlog (Binary Log)

    1. 作用:记录所有对数据库结构或内容进行修改的语句(Statement 模式)或数据行的变更(Row 模式或 Mixed 模式)。主要用于:

      1. 主从复制 (Replication):从库(Slave)通过读取主库(Master)的 Binlog 来重放变更,保持数据同步。

      2. 时间点恢复 (Point-in-Time Recovery, PITR):结合全量备份和 Binlog,可以将数据库恢复到任意历史时间点。

    2. 写入时机:在事务提交时写入(具体是 COMMIT 之前)。

    3. 性质:是 Server 层的逻辑日志,记录的是数据库操作的逻辑变化。

  2. Redo Log (InnoDB 特有)

    1. 作用:记录物理页级别的修改。用于崩溃恢复 (Crash Recovery)。当数据库异常重启时,InnoDB 过重放 Redo Log 中未持久化到数据文件(.ibd)的修改,保证事务的持久性(Durability)。

      写入时机:在事务执行过程中,数据页被修改后,会先写入 Redo Log Buffer,然后按策略(如事务提交时)刷盘(fsync)。

      性质:是存储引擎层的物理日志,记录的是数据页的物理变化。


关键问题:如何保证 Binlog 和 Redo Log 的一致性?

事务的原子性要求:一个事务中的所有操作,要么全部生效(持久化),要么全部不生效(像没发生过一样)。在 MySQL 架构中,一个事务的提交涉及:

  1. 将事务的修改写入存储引擎的数据页和 Redo Log(保证存储引擎内部的持久性)。

  2. 将事务的修改记录写入 Binlog(保证复制和 PITR 的完整性)。

如果 MySQL 没有使用两阶段提交,在事务提交过程中发生崩溃,可能导致 Binlog 和 Redo Log 记录不一致,从而破坏原子性和持久性:

场景一:先写 Binlog,后提交存储引擎(写 Redo Log)

  1. COMMIT 语句开始执行。

  2. Binlog 写入成功(磁盘 fsync)。

  3. 在 InnoDB 真正提交(写 Commit 标记到 Redo Log 并 fsync)之前,数据库崩溃。

    • 后果

      • Binlog 中有该事务的记录。从库会应用这个事务(数据已同步到从库),或者做 PITR 时会包含这个事务。

      • InnoDB 未提交。崩溃恢复时,Redo Log 中没有该事务的 Commit 标记,事务会被回滚。主库上该事务的修改丢失。

    • 问题:主库数据丢失,但从库/PITR 恢复后数据存在。主从不一致!数据不一致! 违反了原子性和持久性。

场景二:先提交存储引擎(写 Redo Log),后写 Binlog

  1. COMMIT 语句开始执行。

  2. InnoDB 提交成功(Redo Log Commit 标记写入并 fsync)。

  3. 在写入 Binlog 之前,数据库崩溃。

    • 后果

      • InnoDB 已提交。崩溃恢复后,事务修改有效存在于主库。

      • Binlog 中没有该事务的记录。从库不会应用这个事务(数据缺失),PITR 也不会包含这个事务。

    • 问题:主库有数据,但从库/PITR 恢复后没有该数据。主从不一致!数据不一致! 同样违反了原子性和持久性(从复制和恢复的角度看,数据没有持久化到整个系统)。


两阶段提交如何解决这个问题?

两阶段提交将事务的提交过程分为两个明确的阶段:

  1. Prepare 阶段 (第一阶段)

    1. InnoDB 将事务的状态设置为 PREPARE,并将包含此状态的 Redo Log 记录强制刷盘(fsync)。

    2. 此时,事务的修改已经安全地持久化在 Redo Log 中(但尚未最终提交),可以保证崩溃后能恢复出这个“准备中”的事务。

    3. 注意:Binlog 此时还没有写入

  2. Commit 阶段 (第二阶段)

    1. MySQL Server (Binlog) 将事务的所有修改写入 Binlog 文件,并强制刷盘(fsync)。这一步确保了 Binlog 的持久性。

    2. 一旦 Binlog 写入并刷盘成功,MySQL Server 通知 InnoDB 进行最终提交。

    3. InnoDB 将事务的状态设置为 COMMIT(通常是在 Redo Log 中写入一个 Commit 标记),并将此记录刷盘。这一步相对较快,因为主要的修改数据已经在 Prepare 阶段持久化了。

崩溃恢复时的处理 - 关键!

当数据库从崩溃中恢复时,InnoDB 会检查 Redo Log:

  • 只有 COMMIT 标记的事务:直接重做(Redo)修改到数据文件。这些事务是完整的,Binlog 肯定也写成功了(因为 Commit 阶段在 Binlog 写完后才发生)。

  • 只有 PREPARE 标记的事务(没有 COMMIT:这是两阶段提交的核心。InnoDB 需要去检查 Binlog

    • 如果该事务对应的 Binlog 记录存在且完整:说明 Prepare 阶段成功,Commit 阶段在写 Binlog 之后、写 InnoDB Commit 标记之前崩溃了。Binlog 已持久化,所以 InnoDB 执行提交(写入 Commit 标记并重做修改)

    • 如果该事务对应的 Binlog 记录不存在或不完整:说明 Prepare 阶段成功,但 Commit 阶段在写 Binlog 之前或中途崩溃了。Binlog 未持久化,所以 InnoDB 执行回滚(Undo 修改)

通过这种机制,两阶段提交确保了:

  1. 原子性:一个事务要么同时在主库(通过 Redo Log 恢复)和 Binlog(复制/PITR)中生效,要么同时在两者中消失。不会出现一个生效另一个不生效的中间状态。

  2. 持久性:一旦客户端收到 COMMIT 成功的响应,数据一定同时安全地持久化在 Redo Log 和 Binlog 中,即使之后发生崩溃。

  3. 数据一致性:主库的数据状态和 Binlog 记录的状态严格一致。

  4. 主从一致性:Binlog 是主从同步的基础。保证 Binlog 和主库数据一致,是保证主从数据一致性的前提。

        MySQL 采用两阶段提交是为了协调其内部两个独立的日志系统(Binlog 和 InnoDB Redo Log)在事务提交过程中的操作。它通过在崩溃恢复阶段根据 Binlog 的完整性来决定是提交还是回滚处于 PREPARE 状态的事务,从而严格保证了无论提交过程中何时发生崩溃,事务在存储引擎层和 Binlog 层最终的状态都是一致的。这是实现数据库高可靠性和数据一致性的基石,尤其是对于主从复制和灾难恢复至关重要。








最新文章
mysql分页问题2025-08-04
千万数据先insert和先建索引哪个快2025-08-04
MySQL 中大小表关联查询如何优化2025-08-04
sql技巧-每个班年龄排前两名的人2025-08-03
MySQL 导致 cpu 飙升的话,要怎么处理呢?2025-07-29
MySQL 中为千万级大表添加字段2025-07-29
mysql中百万级别以上的数据如何删除2025-07-29
分库分表带来的问题2025-07-29
mysql中常用的分库分表中间件有哪些2025-07-29
mysql不停机扩容2025-07-29
备案号:蜀ICP备2023042032号-1