问题: 请区分Redo Log、Undo Log和Binlog各自的用途;说明WAL技术和MySQL内部两阶段提交(2PC)是如何保证事务崩溃恢复时数据一致性的?

👇 点击展开/折叠 面试背诵模板(建议先背这个) Redo Log是InnoDB引擎层的物理日志(记录页级修改),循环写入,用于崩溃恢复,保证事务持久性(Durability)。Undo Log是引擎层的逻辑日志(记录逆操作),用于事务回滚和MVCC,保证原子性(Atomicity)和隔离性。Binlog是Server层的逻辑日志(记录SQL或行变更),追加写入,用于主从复制和基于时间点的数据恢复。WAL(预写日志)要求事务提交时,Redo Log先落盘,数据页后刷盘,将随机I/O转为顺序I/O,确保持久性并提升性能。内部两阶段提交协调Redo Log和Binlog的状态:事务提交时,Redo Log先落盘并标记为Prepare,Binlog落盘后,Redo Log再标记为Commit。崩溃恢复时,若Binlog已完整落盘,则事务视为已提交(提交Redo Log);否则回滚,保证主从数据一致。
---

一、三大日志的职责边界

日志类型 所属层级 日志格式 写入方式 核心用途
Redo Log InnoDB引擎 物理日志(页级修改,如“在页X偏移Y处写入值Z”) 循环写入(固定大小,写满后覆盖) 崩溃恢复(重放已提交事务),保证持久性
Undo Log InnoDB引擎 逻辑日志(逆操作,如“删除对应插入记录”) 存储于表空间的Undo段(可被Purge线程清理) 事务回滚 + MVCC(提供一致性读快照),保证原子性
Binlog MySQL Server层 逻辑日志(Statement/Row/Mixed格式) 追加写入(非循环,可归档) 主从复制 + 基于时间点的增量恢复(PITR)

二、WAL(预写日志)技术的核心价值

WAL(Write-Ahead Logging)要求:在修改数据页之前,必须先将修改操作写入Redo Log,并保证Redo Log在事务提交时落盘(fsync),数据页则延迟到合适时机(检查点)异步刷盘。

  • 性能优化:数据页写入是随机I/O,Redo Log写入是顺序I/O。WAL将随机I/O转换为顺序I/O,极大提升写入吞吐量。
  • 持久性保证:即使数据页未刷盘时宕机,重启后通过重放Redo Log即可恢复到宕机前状态,保证已提交事务的持久性。

三、内部两阶段提交(2PC)的一致性保证机制

为何需要2PC:Redo Log(引擎层)和Binlog(Server层)在逻辑上是独立的。若Redo Log已提交但Binlog未写,主从复制会丢失数据;反之,若Binlog已写但Redo Log未提交,从库数据会多于主库(主从数据不一致)。MySQL通过内部XA事务(两阶段提交)协调两者状态。

两阶段提交具体流程:

  1. Prepare阶段:将事务的Redo Log落盘,并在日志中标记为 Prepare(准备) 状态。
  2. Commit阶段(分为两步):
    • 将事务的Binlog落盘(fsync)。
    • 将Redo Log状态从Prepare改为 Commit(提交)(无需立即落盘,仅更新内存标记)。

崩溃恢复规则(关键):

  • 若重启时发现事务在Redo Log中处于Prepare状态:
    • 检查对应的Binlog是否完整(已落盘且包含该事务)。
    • Binlog已写 → 将该事务提交(重放Redo Log)。
    • Binlog未写 → 将该事务回滚(利用Undo Log撤销修改)。
  • 若Redo Log状态为Commit,则直接提交。

结论:这种机制确保Binlog和Redo Log的最终状态完全一致——要么都包含该事务(提交),要么都不包含(回滚),从而保证主从复制和数据恢复的绝对一致性,实现Crash-Safe能力。