Skip to content

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_logfile0ib_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”(直接记录数据页的物理修改)。

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)

  1. 读取 Checkpoint LSN:确定从哪个 LSN 开始恢复(Checkpoint LSN 之后的 redo log 可能未刷盘)。
  2. 重放 redo log:从 Checkpoint LSN 开始,按顺序应用 redo log entry 到对应数据页(无论事务是否提交,因为未提交事务后续会通过 undo log 回滚)。
  3. 回滚未提交事务:恢复后,通过 undo log 回滚所有未提交的事务(redo log 只负责“重做已提交事务”,未提交事务由 undo log 处理)。

六、redo log 与 binlog 的区别(高频面试题)

维度redo logbinlog
所属层InnoDB 引擎层MySQL Server 层(所有引擎通用)
日志类型物理日志(记录数据页的物理修改)逻辑日志(记录 SQL 逻辑或行修改内容)
写入方式循环写(空间固定,满后覆盖旧日志)追加写(文件大小达 max_binlog_size 后新建文件,不覆盖)
用途崩溃恢复(保证持久性)主从复制、时间点恢复(PITR)
刷盘时机innodb_flush_log_at_trx_commit 控制sync_binlog 控制(默认 0,OS 缓存;1 为每次提交刷盘)

为什么需要两阶段提交(2PC)?

事务提交时需同时写 redo log 和 binlog,为保证两者一致性(“要么都成功,要么都失败”),InnoDB 采用 两阶段提交

  1. Prepare 阶段

    • 写入 redo log,标记为“Prepare”状态,并刷盘(根据 innodb_flush_log_at_trx_commit)。
  2. Commit 阶段

    • 写入 binlog,并刷盘(根据 sync_binlog)。
    • 写入 redo log 的“Commit”标记,并刷盘(仅更新状态,开销小)。

宕机处理

  • 若 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_sizeinnodb_flush_log_at_trx_commit,平衡性能与安全性。