前置:
InnoDB 才有 redo log、undo log;
binlog 是 MySQL Server 层日志,所有引擎都可以产生。
区分:
- redo log:重做日志,保证事务【持久性】(崩溃恢复,已经提交的数据不丢)
- undo log:回滚日志,保证事务【原子性】(事务失败可以回滚,保存数据修改前的旧版本,同时支撑 MVCC)
- binlog:二进制日志,记录数据库【变更事件】,用于主从复制、数据备份恢复
一、redo log(重做日志,InnoDB 引擎层)
作用
- 实现崩溃恢复(crash-safe):事务提交时,先写 redo log(刷盘),再慢慢刷内存脏页到磁盘数据文件。如果数据库宕机,重启后扫描 redo log,把已经提交但还没落盘到 ibd 文件的数据重做。
- 优化磁盘 IO:不是每次修改都直接写磁盘数据文件(随机 IO),而是顺序写 redo log,提升写入性能。
核心:记录「数据页做了什么修改」,记录的是物理页变更 ,不是 SQL 语句。 结构:固定大小的循环文件组(
ib_logfile0、ib_logfile1),环形复用。
写入流程(WAL 预写日志)
WAL:Write-Ahead Logging,先写日志,再写数据
- 修改内存缓冲池 buffer pool 里的数据页(脏页)
- 生成 redo log 记录,写入 redo log buffer
- 事务 commit 时,把 redo log buffer 刷入磁盘 redo log 文件(这一步成功 = 事务持久化成功)
- 后台线程(page cleaner)异步慢慢把脏页刷到磁盘 ibd 数据文件
即使第 4 步没做完、数据库宕机:重启后读 redo log,把这部分变更重做。
场景示例
表 user,id 主键,
update user set name='张三' where id=1;
- id=1 这条记录的数据页加载到 buffer pool 内存
- 内存中把 name 改成张三,该页变成脏页
- InnoDB 生成 redo log:记录「XX 数据页,偏移量 xx 位置,把旧值改成张三」(物理修改)
- commit:redo log 落盘。此时事务提交成功,就算脏页还没写入 ibd 文件也没关系
- 突然断电!内存脏页丢失,但是 redo log 已经保存在磁盘。
- MySQL 重启:读取 redo log,找到这条已经提交的修改,把数据页恢复,保证数据不丢失。
⚠️ redo log 只记录 InnoDB 数据页改动,不能用来回滚事务。
二、undo log(回滚日志,InnoDB 引擎层)
作用
- 原子性:事务回滚:事务执行出错 /rollback 时,用 undo log 把数据恢复成修改前的样子。
- MVCC 多版本并发控制:保存数据修改前的历史快照版本,实现不加锁的读(快照读,select),隔离读。
核心:
记录修改前的数据快照(逻辑记录),不是物理页。
注意:undo log 在事务执行阶段就写入,不是 commit 才写。
场景示例
同样语句:update user set name='张三' where id=1;,原来 name=' 李四'
- 开启事务,读取 id=1 记录
- 先写 undo log:记录这条记录修改前的数据 name = 李四
- 再修改 buffer pool 里数据,生成 redo log
- 情况 A:执行
rollback回滚 InnoDB 读取 undo log 里的旧值 name = 李四,把内存数据恢复回去,事务撤销。 - 情况 B:commit 提交事务 事务提交成功,undo log 不会立刻删除。如果有其他事务此时做快照读,依然可以读到 undo 里保存的旧版本
name=李四,实现 MVCC。 等没有任何事务再引用这个版本,purge 后台线程清理 undo 日志。
关键点:
undo log 用来回滚 + 提供历史版本;
redo log 保证 undo log 本身修改的持久性(修改 undo 页本身也会写 redo log!)
三、binlog(二进制日志,MySQL Server 层)
作用
- 主从复制:主库执行完事务,写入 binlog;从库拉取 binlog,重放 SQL 实现数据同步。
- 时间点数据备份恢复:全量备份 + 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 格式
- 事务执行,修改数据,生成 undo、redo
- commit 阶段:
- 写 redo log(prepare 阶段)
- 写入 binlog
- redo log 标记 commit
- binlog 记录:id=1 这一行,旧 name = 李四,新 name = 张三(行变更记录)
- 主从场景:从库同步拉取 binlog,重放这行变更,从库数据同步更新。
- 数据误删恢复场景: 比如 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 / 行变更) | 主从复制、时间点备份 | 追加写入,文件滚动,手动清理 |
五、完整事务执行串联例子(三合一)
事务:
sqlbegin; update user set name='张三' where id=1; --原值name='李四' commit;
- begin 开启事务
- 找到 id=1 行,写入 undo log,保存旧值 name = 李四
- buffer pool 修改内存记录为 name = 张三,生成 redo log(记录数据页物理变更)
- commit 进入 2PC:
- redo log prepare 落盘
- 写 binlog(记录行变更:李四→张三)落盘
- redo log 标记 commit
- 事务提交成功
- 脏页还在内存,后续 page cleaner 异步刷 ibd 文件
- undo log 保留一段时间,给其他事务快照读,后续 purge 清理
- binlog 文件保留,给从库同步 / 备份
- 如果此时断电:
- MySQL 重启,扫描 redo log,发现这条 prepare 成功、binlog 也写入完成,自动提交事务,数据持久化。
- 如果 prepare 阶段断电,binlog 没写成功,事务回滚。
六、总结
- redo log:WAL,crash-safe,物理页,引擎层,循环写。持久性
- undo log:回滚 + MVCC,保存旧版本,逻辑记录,引擎层。原子性 + 隔离
- binlog:server 层,逻辑日志,主从、备份,追加写。复制、时间点恢复
- 2PC:保证 redo 和 binlog 两个日志的事务一致性,防止主从数据不一致
- 一句话区分:
- 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。
✅ 物理日志特点:
- 操作对象是磁盘数据页,不是行、不是 SQL;
- 恢复时:直接把日志里的字节覆盖写到对应数据页;
- 只属于 InnoDB 引擎,server 层看不到;
- 幂等:重复回放多少次,结果都一样。
补充:redo log 是物理日志,但带少量行信息,行业习惯统一归类为物理日志,不要纠结 "纯物理" 这个字眼。
2. undo log(回滚日志)------ 逻辑日志
事务回滚、MVCC 读快照用,逻辑日志。
同样这条 update:
update user set name='B' where id=1;
undo log 记录:
id=1, name原来的值是'A'
不是记录磁盘哪个字节,而是记录逻辑上旧行数据 。 事务要回滚的时候,执行一个反向逻辑操作:把 name 改回 A。
✅ 逻辑日志特点:
- 记录数据变更的逻辑内容(旧行),不是磁盘字节;
- 回滚不是 "擦除磁盘页",而是执行反向逻辑操作;
- 属于 InnoDB 内部日志。
重点区分 redo vs undo: redo:新值,物理,崩溃恢复; undo:旧值,逻辑,事务回滚 / MVCC。
3. binlog(二进制日志)------ 逻辑日志(MySQL Server 层)
主从复制、数据备份用,在 MySQL Server 层,和存储引擎无关。
同样 update 语句,binlog 有 3 种格式:
-
statement 格式(语句级逻辑):直接记录 SQL
update user set name='B' where id=1;
-
row 格式(行级逻辑,生产默认):记录哪一行,修改前和修改后数据
表user,id=1:name由'A' → 'B'
-
mixed:混合上面两种
binlog不会记录磁盘页、偏移、字节 。 主库同步给从库,从库拿到 binlog,解析这条逻辑变更,在自己引擎里重新执行修改行。
✅ binlog 逻辑日志特点:
- Server 层日志,InnoDB/MyISAM 都可以产生;
- 记录的是业务逻辑变更(SQL 或者行变更);
- 主从复制靠它;
- row 模式下是行级逻辑,依然不是物理页。
汇总对比表
表格
| 日志 | 类型 | 记录内容 | 作用 |
|---|---|---|---|
| redo log | 物理日志 | 数据页 + 偏移 + 新字节 | 崩溃恢复,保证刷盘前数据安全 |
| undo log | 逻辑日志 | 修改前旧行数据 | 事务回滚、MVCC |