MySQL 日志系统 redo log、undo log、binlog 有什么区别?

2025年 阅读约 15 分钟 面试指南 · MySQL

深入解析MySQL三大日志:redo log(崩溃恢复,WAL机制)、undo log(事务回滚,MVCC版本链)、binlog(主从复制,数据恢复),两阶段提交保证一致性,附面试模拟问答。

一句话总结

MySQL InnoDB 有三大日志:redo log(重做日志)— 物理日志,记录"数据页做了什么修改",用于崩溃恢复,实现 WAL 机制;undo log(回滚日志)— 逻辑日志,记录"修改前的数据",用于事务回滚和 MVCC;binlog(归档日志)— Server 层逻辑日志,记录所有 SQL 语句,用于主从复制和数据恢复。redo log 和 binlog 通过两阶段提交保证一致性。

初级理解

三大日志对比

对比维度redo logundo logbinlog
所属层InnoDB 引擎层InnoDB 引擎层MySQL Server 层
日志类型物理日志逻辑日志逻辑日志
记录内容数据页的物理修改修改前的数据(回滚用)SQL 语句或行变更
写入方式循环写(固定大小)随机写(追加)追加写
主要作用崩溃恢复(crash-safe)事务回滚 + MVCC主从复制 + 数据恢复
可否关闭不可关闭不可关闭可关闭(不推荐)
一句话总结:redo log 保持久(崩溃恢复),undo log 保原子(回滚),binlog 保复制(主从同步)。

中级深入

redo log — WAL 机制与崩溃恢复

WAL(Write-Ahead Logging):先写日志,再写磁盘。事务提交时,先写 redo log,再异步刷脏页到磁盘。这样即使刷脏页前宕机,重启后也能通过 redo log 恢复数据。

# redo log 配置 SHOW VARIABLES LIKE 'innodb_log%'; # innodb_log_file_size: 单个 redo log 文件大小(默认 48M) # innodb_log_files_in_group: redo log 文件数量(默认 2) # innodb_log_buffer_size: redo log 缓冲区大小 # redo log 写入策略(innodb_flush_log_at_trx_commit) # 0: 每秒刷一次(性能最好,可能丢 1 秒数据) # 1: 每次提交都刷盘(默认,最安全) # 2: 每次提交写 OS 缓存,每秒刷盘(折中) # 循环写示意 # redo log 是固定大小的环形结构 # write pos: 当前写入位置 # checkpoint: 已刷盘位置 # write pos 追上 checkpoint → 需要等待刷盘(可能阻塞)

binlog — 主从复制与数据恢复

# binlog 配置 SHOW VARIABLES LIKE 'log_bin%'; SHOW VARIABLES LIKE 'binlog%'; # sync_binlog: binlog 刷盘策略 # 0: 由 OS 决定何时刷盘 # 1: 每次提交都刷盘(推荐,最安全) # N: 每 N 次提交刷一次盘 # 数据恢复示例 # 1. 全量备份 + binlog 恢复到指定时间点 mysqlbinlog --start-datetime="2025-01-01 00:00:00" \ --stop-datetime="2025-01-01 12:00:00" \ mysql-bin.000001 | mysql -u root -p # 2. 恢复到指定位置 mysqlbinlog --start-position=1234 --stop-position=5678 \ mysql-bin.000001 | mysql -u root -p

两阶段提交 — redo log 与 binlog 的一致性

为什么需要两阶段提交?因为 redo log 和 binlog 是两个独立的系统,如果写了一个另一个没写,会导致主从不一致。

# 两阶段提交流程 # 1. Prepare 阶段:写 redo log,标记为 prepare 状态 # 2. 写 binlog # 3. Commit 阶段:写 redo log,标记为 commit 状态 # 崩溃恢复判断: # - redo log 中有 commit 标记 → 事务已提交,直接恢复 # - redo log 中有 prepare 标记,binlog 中有对应记录 → 提交 # - redo log 中有 prepare 标记,binlog 中无对应记录 → 回滚 # 为什么先写 redo log 再写 binlog? # 如果先写 binlog 再写 redo log: # binlog 写了但 redo log 没写 → 崩溃后从库有数据,主库没有 → 不一致
中级要点:redo log 循环写保证崩溃恢复;binlog 追加写保证复制;两阶段提交保证两者一致。

高级拓展

一条 UPDATE 语句的执行流程(含日志)

# UPDATE users SET name='李四' WHERE id=1; # 1. 执行器调用 InnoDB 引擎接口,读取 id=1 的行 # 2. InnoDB 从 Buffer Pool 中查找,没有则从磁盘加载 # 3. 执行器修改 name='李四',调用引擎写入接口 # 4. InnoDB 将修改前的数据写入 undo log(用于回滚和 MVCC) # 5. InnoDB 修改 Buffer Pool 中的数据页(脏页) # 6. InnoDB 将修改操作写入 redo log buffer # 7. 执行器写 binlog(binlog cache → binlog 文件) # 8. 提交事务: # a. redo log prepare # b. 写 binlog # c. redo log commit # 9. 返回客户端"更新成功" # 10. 后台线程异步将脏页刷入磁盘

redo log 块满了怎么办?

redo log 是循环写的固定大小文件。当 write pos 追上 checkpoint 时,需要等待 checkpoint 推进(将脏页刷盘),此时所有写操作被阻塞。这就是为什么 redo log 不能太小——太小会导致频繁刷盘,影响性能。

# 查看 redo log 使用情况 SHOW ENGINE INNODB STATUS\G # 找到 LOG 部分: # Log sequence number: 当前 LSN # Log flushed up to: 已刷到磁盘的 LSN # Pages flushed up to: 已刷脏页的 LSN # Last checkpoint at: checkpoint 位置

实战场景

场景:误删数据恢复

# 前提:有全量备份 + binlog 开启 # 1. 恢复全量备份到临时库 mysql -u root -p temp_db < backup.sql # 2. 找到误删操作的 binlog 位置 mysqlbinlog --start-datetime="2025-01-01 10:00:00" \ mysql-bin.000001 | grep -i "DELETE FROM users" # 3. 恢复 binlog 到误删前一刻 mysqlbinlog --stop-datetime="2025-01-01 10:29:59" \ mysql-bin.000001 | mysql -u root -p temp_db # 4. 导出恢复的数据 mysqldump -u root -p temp_db users > users_recovered.sql # 5. 导入回生产库 mysql -u root -p prod_db < users_recovered.sql

面试模拟

面试官:redo log 和 binlog 有什么区别?

你:1)redo log 是 InnoDB 引擎层的物理日志,记录数据页修改,循环写;binlog 是 Server 层的逻辑日志,记录 SQL 语句,追加写。2)redo log 用于崩溃恢复(crash-safe),binlog 用于主从复制和数据恢复。3)redo log 在事务执行过程中不断写入,binlog 在事务提交时才写入。4)两者通过两阶段提交保证一致性。

面试官:为什么要有两阶段提交?

你:因为 redo log 和 binlog 是两个独立的系统,如果写了一个另一个没写,会导致主从不一致。两阶段提交确保:要么两个都写成功(事务提交),要么两个都没写(事务回滚)。崩溃恢复时,如果 redo log 是 prepare 状态且 binlog 有对应记录就提交,否则回滚。