详解redo log、undo log、binlog(附场景示例)

前置:

InnoDB 才有 redo log、undo log;

binlog 是 MySQL Server 层日志,所有引擎都可以产生。

区分:

  • redo log:重做日志,保证事务【持久性】(崩溃恢复,已经提交的数据不丢)
  • undo log:回滚日志,保证事务【原子性】(事务失败可以回滚,保存数据修改前的旧版本,同时支撑 MVCC)
  • binlog:二进制日志,记录数据库【变更事件】,用于主从复制、数据备份恢复

一、redo log(重做日志,InnoDB 引擎层)

作用

  1. 实现崩溃恢复(crash-safe):事务提交时,先写 redo log(刷盘),再慢慢刷内存脏页到磁盘数据文件。如果数据库宕机,重启后扫描 redo log,把已经提交但还没落盘到 ibd 文件的数据重做。
  2. 优化磁盘 IO:不是每次修改都直接写磁盘数据文件(随机 IO),而是顺序写 redo log,提升写入性能。

核心:记录「数据页做了什么修改」,记录的是物理页变更 ,不是 SQL 语句。 结构:固定大小的循环文件组(ib_logfile0、ib_logfile1),环形复用。

写入流程(WAL 预写日志)

WAL:Write-Ahead Logging,先写日志,再写数据

  1. 修改内存缓冲池 buffer pool 里的数据页(脏页)
  2. 生成 redo log 记录,写入 redo log buffer
  3. 事务 commit 时,把 redo log buffer 刷入磁盘 redo log 文件(这一步成功 = 事务持久化成功)
  4. 后台线程(page cleaner)异步慢慢把脏页刷到磁盘 ibd 数据文件

即使第 4 步没做完、数据库宕机:重启后读 redo log,把这部分变更重做。

场景示例

表 user,id 主键,update user set name='张三' where id=1;

  1. id=1 这条记录的数据页加载到 buffer pool 内存
  2. 内存中把 name 改成张三,该页变成脏页
  3. InnoDB 生成 redo log:记录「XX 数据页,偏移量 xx 位置,把旧值改成张三」(物理修改)
  4. commit:redo log 落盘。此时事务提交成功,就算脏页还没写入 ibd 文件也没关系
  5. 突然断电!内存脏页丢失,但是 redo log 已经保存在磁盘。
  6. MySQL 重启:读取 redo log,找到这条已经提交的修改,把数据页恢复,保证数据不丢失。

⚠️ redo log 只记录 InnoDB 数据页改动,不能用来回滚事务。


二、undo log(回滚日志,InnoDB 引擎层)

作用

  1. 原子性:事务回滚:事务执行出错 /rollback 时,用 undo log 把数据恢复成修改前的样子。
  2. MVCC 多版本并发控制:保存数据修改前的历史快照版本,实现不加锁的读(快照读,select),隔离读。

核心:

记录修改前的数据快照(逻辑记录),不是物理页。
注意:

undo log 在事务执行阶段就写入,不是 commit 才写。

场景示例

同样语句:update user set name='张三' where id=1;,原来 name=' 李四'

  1. 开启事务,读取 id=1 记录
  2. 先写 undo log:记录这条记录修改前的数据 name = 李四
  3. 再修改 buffer pool 里数据,生成 redo log
  4. 情况 A:执行rollback回滚 InnoDB 读取 undo log 里的旧值 name = 李四,把内存数据恢复回去,事务撤销。
  5. 情况 B:commit 提交事务 事务提交成功,undo log 不会立刻删除。如果有其他事务此时做快照读,依然可以读到 undo 里保存的旧版本name=李四,实现 MVCC。 等没有任何事务再引用这个版本,purge 后台线程清理 undo 日志。

关键点:

undo log 用来回滚 + 提供历史版本;

redo log 保证 undo log 本身修改的持久性(修改 undo 页本身也会写 redo log!)


三、binlog(二进制日志,MySQL Server 层)

作用

  1. 主从复制:主库执行完事务,写入 binlog;从库拉取 binlog,重放 SQL 实现数据同步。
  2. 时间点数据备份恢复:全量备份 + binlog,可以恢复到任意时间点。

核心:逻辑日志,记录 SQL 事件(DML/DDL),不是物理页。 三种格式:

  • statement:记录原始 SQL(简单,但函数、随机函数会有主从不一致问题)
  • row(推荐):记录行变更前后数据,和 SQL 无关,主从最安全,日志体积大
  • mixed:混合模式

写入时机

事务 commit 时,在 redo log 之后写入 binlog,有两阶段提交 2PC 保证 redo log 和 binlog 一致性,防止 redo 写成功 binlog 失败或者反过来,导致主从数据不一致。

场景示例

update user set name='张三' where id=1;,binlog 为 row 格式

  1. 事务执行,修改数据,生成 undo、redo
  2. commit 阶段:
    1. 写 redo log(prepare 阶段)
    2. 写入 binlog
    3. redo log 标记 commit
  3. binlog 记录:id=1 这一行,旧 name = 李四,新 name = 张三(行变更记录)
  4. 主从场景:从库同步拉取 binlog,重放这行变更,从库数据同步更新。
  5. 数据误删恢复场景: 比如 10:00 执行 update 改错数据。我们用之前的全量备份恢复到 9:00,然后用 binlog 回放 9:00~10:00 之间日志,跳过错误那条 SQL,恢复数据。

binlog不负责崩溃恢复,宕机后不会用 binlog 恢复当前实例数据;

崩溃恢复只靠 redo log。

四、三者横向对比表

日志 所属层 日志类型 核心目的 生命周期
redo log InnoDB 引擎层 物理日志(数据页改动) 崩溃恢复,持久性 循环覆盖
undo log InnoDB 引擎层 逻辑快照(修改前数据) 事务回滚、MVCC 事务提交后延迟 purge 清理
binlog MySQL Server 层 逻辑日志(SQL / 行变更) 主从复制、时间点备份 追加写入,文件滚动,手动清理

五、完整事务执行串联例子(三合一)

事务:

sql 复制代码
begin;
update user set name='张三' where id=1; --原值name='李四'
commit;
  1. begin 开启事务
  2. 找到 id=1 行,写入 undo log,保存旧值 name = 李四
  3. buffer pool 修改内存记录为 name = 张三,生成 redo log(记录数据页物理变更)
  4. commit 进入 2PC:
    • redo log prepare 落盘
    • 写 binlog(记录行变更:李四→张三)落盘
    • redo log 标记 commit
  5. 事务提交成功
    • 脏页还在内存,后续 page cleaner 异步刷 ibd 文件
    • undo log 保留一段时间,给其他事务快照读,后续 purge 清理
    • binlog 文件保留,给从库同步 / 备份
  6. 如果此时断电:
    • MySQL 重启,扫描 redo log,发现这条 prepare 成功、binlog 也写入完成,自动提交事务,数据持久化。
    • 如果 prepare 阶段断电,binlog 没写成功,事务回滚。

六、总结

  1. redo log:WAL,crash-safe,物理页,引擎层,循环写。持久性
  2. undo log:回滚 + MVCC,保存旧版本,逻辑记录,引擎层。原子性 + 隔离
  3. binlog:server 层,逻辑日志,主从、备份,追加写。复制、时间点恢复
  4. 2PC:保证 redo 和 binlog 两个日志的事务一致性,防止主从数据不一致
  5. 一句话区分:
    • redo:怕宕机丢已经提交的数据
    • undo:可以反悔回滚,给 MVCC 读旧数据
    • binlog:给别的库(从库)同步,做备份
  • 物理日志 :记录「磁盘上哪个位置、改成什么数据」,面向数据页 / 磁盘块。
  • 逻辑日志 :记录「执行了什么 SQL 操作、要修改哪行数据」,面向行 / 业务逻辑。

MySQL InnoDB 三大日志:

  • redo log:物理日志(偏物理)
  • undo log:逻辑日志
  • binlog:逻辑日志

1. redo log(重做日志)------ 物理日志

InnoDB 的崩溃恢复用,属于物理日志 ,记录的是数据页的修改,不是 SQL,也不是行记录。

举例子: 表 user,id=1,name='A',执行:

复制代码
update user set name='B' where id=1;

redo log 不会记 update user set name='B' where id=1; 也不会简单记 id=1 name从A改成B。

redo log 记录的是:

表空间号 + 数据页号 + 页内偏移量 + 修改后的字节内容 (space:1, page:100, offset:20, new_value='B')

含义:磁盘上这个数据页的这个位置,把字节改成 B。

✅ 物理日志特点:

  1. 操作对象是磁盘数据页,不是行、不是 SQL;
  2. 恢复时:直接把日志里的字节覆盖写到对应数据页;
  3. 只属于 InnoDB 引擎,server 层看不到;
  4. 幂等:重复回放多少次,结果都一样。

补充:redo log 是物理日志,但带少量行信息,行业习惯统一归类为物理日志,不要纠结 "纯物理" 这个字眼。


2. undo log(回滚日志)------ 逻辑日志

事务回滚、MVCC 读快照用,逻辑日志。

同样这条 update:

复制代码
update user set name='B' where id=1;

undo log 记录:

id=1, name原来的值是'A'

不是记录磁盘哪个字节,而是记录逻辑上旧行数据 。 事务要回滚的时候,执行一个反向逻辑操作:把 name 改回 A。

✅ 逻辑日志特点:

  1. 记录数据变更的逻辑内容(旧行),不是磁盘字节;
  2. 回滚不是 "擦除磁盘页",而是执行反向逻辑操作;
  3. 属于 InnoDB 内部日志。

重点区分 redo vs undo: redo:新值,物理,崩溃恢复; undo:旧值,逻辑,事务回滚 / MVCC。


3. binlog(二进制日志)------ 逻辑日志(MySQL Server 层)

主从复制、数据备份用,在 MySQL Server 层,和存储引擎无关。

同样 update 语句,binlog 有 3 种格式:

  1. statement 格式(语句级逻辑):直接记录 SQL

    update user set name='B' where id=1;

  2. row 格式(行级逻辑,生产默认):记录哪一行,修改前和修改后数据

    表user,id=1:name由'A' → 'B'

  3. mixed:混合上面两种

binlog不会记录磁盘页、偏移、字节 。 主库同步给从库,从库拿到 binlog,解析这条逻辑变更,在自己引擎里重新执行修改行。

✅ binlog 逻辑日志特点:

  1. Server 层日志,InnoDB/MyISAM 都可以产生;
  2. 记录的是业务逻辑变更(SQL 或者行变更);
  3. 主从复制靠它;
  4. row 模式下是行级逻辑,依然不是物理页。

汇总对比表

表格

日志 类型 记录内容 作用
redo log 物理日志 数据页 + 偏移 + 新字节 崩溃恢复,保证刷盘前数据安全
undo log 逻辑日志 修改前旧行数据 事务回滚、MVCC
相关推荐
for_ever_love__19 小时前
MySQL 主从复制讲透:binlog 原理、搭建步骤与读写分离
java·数据库·mysql·binlog·读写分离·原理·主从复制
海市公约5 个月前
MySQL更新语句执行全流程:从Buffer Pool修改到二阶段提交
数据库·mysql·binlog·innodb·undo log·二阶段提交·update执行原理
庞轩px5 个月前
第六篇:Redo Log与Binlog——崩溃恢复的底层保障
mysql·binlog·数据安全·innodb·日志·redo log·update
九皇叔叔5 个月前
MySQL 8.x Binlog 核心实操:查看、切换、清理
android·mysql·adb·binlog
qq_283720056 个月前
MySQL技巧(九): Binlog 完整格式解析(ROW 模式,默认)
mysql·binlog·数据恢复
LF3_7 个月前
监听数据库binlog日志变化,将变动实时发送到kafka
数据库·分布式·mysql·kafka·binlog·debezium
予枫的编程笔记8 个月前
【MySQL飞升篇】MySQL主从复制灵魂三问:Binlog怎么选?线程如何工作?延迟怎么解?
mysql·性能优化·binlog·主从复制·数据库运维·并行复制·延迟解决
IT利刃出鞘8 个月前
MySQL--binlog日志手动删除与自动清理
mysql·binlog
JAVA拾贝9 个月前
全链路数据监控 Binlog View
mysql·canal·binlog·binlog view·数据链路监控