Skip to content

概念卡片:MySQL 日志与两阶段提交

一句话机制

MySQL 更新靠 WAL(先写日志再写磁盘):InnoDB 的 redo log(物理日志,崩溃恢复)+ Server 层的 binlog(逻辑日志,归档/主从),两者用「两阶段提交」保证逻辑一致。 这是「数据库异常重启数据不丢」和「主从一致」的根基。

三大日志对比

日志归属内容写入作用
redo logInnoDB 特有物理日志(某数据页改了什么)循环写(固定空间,写满擦除)crash-safe 崩溃恢复
binlogServer 层(所有引擎)逻辑日志(语句原始逻辑)追加写(写满换文件)归档/主从复制/数据恢复
undo logInnoDB记录回滚操作事务回滚 + MVCC 多版本

redo log 的循环写

write pos(当前写位置)→ 一直后移
checkpoint(当前擦除位置)→ 擦除前先刷盘
两者之间 = 可写空间;write pos 追上 checkpoint = 写满,需先推进 checkpoint

WAL(Write-Ahead Logging)

先写日志、再写磁盘:更新时先把记录写 redo log + 更新内存,后台空闲时再刷数据页。把「随机写数据页」变成「顺序写日志」,性能大幅提升。

两阶段提交(为什么必须)

一条 UPDATE 的完整流程:

1. 执行器取数据(内存有直接用,否则读盘)
2. 执行器改值,调引擎写接口
3. InnoDB:更新内存 + 写 redo log(状态 = prepare)  ← 阶段1
4. 执行器:写 binlog 到磁盘
5. InnoDB:redo log 状态改 commit                        ← 阶段2

为什么不用「先写 redo 再写 binlog」或反过来? 都会导致崩溃后「库的状态」和「日志恢复出来的库」不一致:

  • 先写 redo 后写 binlog:redo 写完 binlog 没写就 crash → 库里 c=1(redo 恢复了),但 binlog 没记录,用 binlog 恢复的临时库 c=0 → 不一致
  • 先写 binlog 后写 redo:binlog 写完 redo 没写就 crash → 库 c=0(事务无效),但 binlog 记录了,恢复库 c=1 → 不一致

两阶段提交保证 redo log 和 binlog 逻辑一致,主从才不会漂移。

保证不丢数据的两个参数

参数设为 1 的含义
innodb_flush_log_at_trx_commit=1每次事务 redo log 直接刷盘
sync_binlog=1每次事务 binlog 直接刷盘

两个都设 1,异常重启数据不丢(代价是每次事务两次刷盘,性能略降)。

常见误解(避坑)

  1. ❌ "redo log 和 binlog 可以只用一个"。→ 一个管崩溃恢复(引擎)、一个管归档主从(Server),职责不同,缺一不可。
  2. ❌ "redo log 能无限写"。→ 循环写、空间固定,写满要擦除(推进 checkpoint)。
  3. ❌ "两阶段提交是为了性能"。→ 是为了一致性(redo/binlog 逻辑一致),性能是 WAL 带来的。
  4. ❌ "binlog 是物理日志"。→ binlog 是逻辑日志(记语句逻辑),redo log 才是物理日志。

关联

最近更新