Appearance
MySQL redo log 深度解析:原理、机制与实践
一、redo log 是什么?核心作用是什么?
redo log(重做日志)是 InnoDB 存储引擎特有的日志,用于保证事务的 持久性(Durability,ACID 中的 D)。其核心作用是:在数据库宕机后,通过重放 redo log 恢复未刷盘的已提交事务数据,避免数据丢失。
二、为什么需要 redo log?—— 从“直接刷盘”的痛点说起
InnoDB 数据以 页(Page,默认 16KB) 为单位存储在磁盘,而内存中维护了 缓冲池(Buffer Pool) 作为数据页的缓存。当事务修改数据时,InnoDB 会先修改 Buffer Pool 中的数据页(称为“脏页”),再异步将脏页刷回磁盘(减少随机 IO,提升性能)。
问题:若 Buffer Pool 中的脏页尚未刷盘时数据库宕机,已提交的事务数据会丢失,违背事务持久性。
解决方案:引入 redo log,在修改数据页前先记录“物理修改日志”(即“日志先行写”原则,Write-Ahead Logging, WAL)。即使数据页未刷盘,只要 redo log 已持久化,宕机后可通过 redo log 重放修改,恢复数据。
三、redo log 的核心结构
redo log 由 内存组件 和 磁盘组件 两部分组成,通过 LSN(Log Sequence Number,日志序列号) 串联。
1. 内存组件:redo log buffer
- 作用:临时存储事务执行过程中产生的 redo log 记录,减少直接写磁盘的 IO 开销。
- 大小控制:通过
innodb_log_buffer_size参数配置(默认 16MB,可根据事务量调整,如大事务场景设为 64MB/128MB)。 - 写入时机:事务执行过程中,每次修改操作(如
INSERT/UPDATE/DELETE)会生成 redo log 记录,直接写入 redo log buffer(无需等待事务提交)。
2. 磁盘组件:redo log file(重做日志文件)
- 文件组成:默认由 2 个文件(
ib_logfile0、ib_logfile1)组成 循环写入的日志文件组(数量由innodb_log_files_in_group控制,默认 2)。 - 文件大小:单个文件大小由
innodb_log_file_size控制(默认 48MB,总大小 = 单个大小 × 文件数)。 - 循环写入:日志从第一个文件开始写,写满后切换到下一个,全部写满后覆盖最早的文件(类似“环形缓冲区”)。
3. LSN(Log Sequence Number):日志序列号
LSN 是一个 单调递增的 64 位整数,用于标记 redo log 的逻辑顺序,是 redo log 核心中的核心。主要存在于 4 个位置:
- redo log buffer LSN:当前内存中已生成的 redo log 总量(
Log sequence number,可通过SHOW ENGINE INNODB STATUS查看)。 - redo log file LSN:磁盘上已持久化的 redo log 总量(
Log flushed up to)。 - 数据页 LSN(Page LSN):数据页最后一次修改并刷盘的 LSN(存储在数据页头部)。
- Checkpoint LSN:已刷盘到磁盘的数据页对应的最大 LSN(
Last checkpoint at)。
作用:通过比较不同 LSN 的大小,判断数据是否需要恢复(如数据页 LSN < redo log LSN → 需重放 redo log)。
四、redo log 的工作流程(WAL 原则落地)
事务从执行到提交的完整流程中,redo log 的行为如下:
1. 事务执行阶段
- 事务修改数据时,先在 Buffer Pool 中修改数据页(生成脏页)。
- 同时,将修改操作记录为 redo log entry(重做日志记录),写入 redo log buffer。
- redo log entry 是 物理日志,格式类似:
“表空间 ID=1,页号=100,偏移量=200,写入 4 字节数据:0x12345678”(直接记录数据页的物理修改)。
- redo log entry 是 物理日志,格式类似:
2. 事务提交阶段(核心!WAL 落地)
事务提交时,必须先将 redo log buffer 中的日志持久化到磁盘(redo log file),再标记事务提交成功(日志先行写)。具体刷盘策略由 innodb_flush_log_at_trx_commit 参数控制:
| 参数值 | 含义 | 安全性 | 性能 |
|---|---|---|---|
| 0 | 事务提交时不刷盘,由后台线程每秒刷 1 次 | 最差(可能丢失 1 秒内事务) | 最好 |
| 1(默认) | 事务提交时,redo log 从 buffer 同步写入磁盘(调用 fsync) | 最好(完全保证持久性) | 较差(每次提交触发 fsync) |
| 2 | 事务提交时,仅写入操作系统缓存(Page Cache),由 OS 决定何时刷盘 | 中等(OS 宕机可能丢失数据,数据库宕机不丢) | 中等 |
生产建议:为保证 ACID 持久性,优先使用 innodb_flush_log_at_trx_commit=1。
3. 日志刷盘细节
redo log buffer 刷盘到 redo log file 时,不是每次提交都直接写磁盘,而是通过 组提交(Group Commit) 优化:多个事务的 redo log 合并后一次刷盘,减少 fsync 次数(InnoDB 5.6+ 支持)。
五、redo log 的崩溃恢复机制
数据库宕机重启时,InnoDB 会自动执行恢复流程,核心是通过 redo log 重放未刷盘的已提交事务:
1. 恢复触发条件
- 数据页 LSN < redo log LSN:表示数据页未完全刷盘,需通过 redo log 恢复。
2. 恢复流程(前滚,Roll Forward)
- 读取 Checkpoint LSN:确定从哪个 LSN 开始恢复(Checkpoint LSN 之后的 redo log 可能未刷盘)。
- 重放 redo log:从 Checkpoint LSN 开始,按顺序应用 redo log entry 到对应数据页(无论事务是否提交,因为未提交事务后续会通过 undo log 回滚)。
- 回滚未提交事务:恢复后,通过 undo log 回滚所有未提交的事务(redo log 只负责“重做已提交事务”,未提交事务由 undo log 处理)。
六、redo log 与 binlog 的区别(高频面试题)
| 维度 | redo log | binlog |
|---|---|---|
| 所属层 | InnoDB 引擎层 | MySQL Server 层(所有引擎通用) |
| 日志类型 | 物理日志(记录数据页的物理修改) | 逻辑日志(记录 SQL 逻辑或行修改内容) |
| 写入方式 | 循环写(空间固定,满后覆盖旧日志) | 追加写(文件大小达 max_binlog_size 后新建文件,不覆盖) |
| 用途 | 崩溃恢复(保证持久性) | 主从复制、时间点恢复(PITR) |
| 刷盘时机 | 由 innodb_flush_log_at_trx_commit 控制 | 由 sync_binlog 控制(默认 0,OS 缓存;1 为每次提交刷盘) |
为什么需要两阶段提交(2PC)?
事务提交时需同时写 redo log 和 binlog,为保证两者一致性(“要么都成功,要么都失败”),InnoDB 采用 两阶段提交:
Prepare 阶段:
- 写入 redo log,标记为“Prepare”状态,并刷盘(根据
innodb_flush_log_at_trx_commit)。
- 写入 redo log,标记为“Prepare”状态,并刷盘(根据
Commit 阶段:
- 写入 binlog,并刷盘(根据
sync_binlog)。 - 写入 redo log 的“Commit”标记,并刷盘(仅更新状态,开销小)。
- 写入 binlog,并刷盘(根据
宕机处理:
- 若 Prepare 后、Commit 前宕机:重启后检查 binlog,若 binlog 存在该事务 → 提交;否则 → 回滚。
- 保证了 redo log 和 binlog 的数据一致性(主从复制时避免数据不一致)。
七、Checkpoint 机制:控制 redo log 循环写入
redo log 文件大小固定,若无限写入会占满磁盘。Checkpoint 机制 通过定期刷盘脏页,推进 Checkpoint LSN,使旧日志可被覆盖。
Checkpoint 类型
- Sharp Checkpoint:数据库正常关闭时,将所有脏页刷盘(全量刷盘)。
- Fuzzy Checkpoint:运行时刷部分脏页(增量刷盘),包括:
- Master Thread Checkpoint:主线程每秒/每 10 秒刷少量脏页。
- Async/Sync Flush Checkpoint:redo log 快写满时(如剩余空间 < 75%),触发刷脏页以推进 Checkpoint。
- Dirty Page too much Checkpoint:脏页比例 >
innodb_max_dirty_pages_pct(默认 75%)时触发刷盘。
八、redo log 配置优化
1. 核心参数
innodb_log_file_size:单个 redo log 文件大小(推荐 1GB~4GB,太大影响恢复速度,太小导致 Checkpoint 频繁)。innodb_log_files_in_group:日志文件数量(默认 2,建议 2~4 个,避免单文件故障)。innodb_log_buffer_size:redo log buffer 大小(大事务场景调大,如 64MB,减少刷盘次数)。innodb_flush_log_at_trx_commit:刷盘策略(建议设为 1,保证持久性)。
2. 优化建议
- 避免 redo log 文件过小:若
innodb_log_file_size太小,会导致 Async Flush Checkpoint 频繁触发,脏页刷盘密集,IO 压力大。 - 合理设置 buffer 大小:若业务有大量大事务(如批量插入),调大
innodb_log_buffer_size减少刷盘次数。 - 监控 LSN 状态:通过
SHOW ENGINE INNODB STATUS查看Log sequence number(buffer LSN)和Log flushed up to(磁盘 LSN),若差距过大(如 > 100MB),可能是刷盘瓶颈,需优化innodb_flush_log_at_trx_commit或磁盘 IO。
九、总结
redo log 是 InnoDB 保证事务持久性的核心机制,通过“日志先行写”(WAL)和“物理日志”特性,在减少磁盘 IO 的同时,确保宕机后数据可恢复。理解 redo log 的结构(buffer/file/LSN)、工作流程(事务提交刷盘)、恢复机制(LSN 比较+前滚)及与 binlog 的协同(两阶段提交),是深入掌握 MySQL 存储引擎的关键。
关键 takeaway:
- redo log 解决“脏页未刷盘宕机”的数据丢失问题,是 ACID 持久性的基石。
- LSN 是串联内存日志、磁盘日志、数据页的核心标识。
- 两阶段提交保证 redo log 和 binlog 的一致性,支撑主从复制。
- 合理配置
innodb_log_file_size和innodb_flush_log_at_trx_commit,平衡性能与安全性。
