目录
- MySQL 三种日志?
- 二进制日志(Binlog)
- 重做日志(Redo Log)
- 回滚日志(Undo Log)
- 其它辅助日志
- 总结
- redo log 与 undo log 的区别?
- undo log 是如何实现 MVCC 的?
- redo log 与 binlog 的区别?
- 为什么有 binlog,还需要有 redo log?
- 什么是 crash-safe?
- 什么是 WAL 技术?
- 被修改的 undo 页面,需要记录对应的 redo log 吗?
- binlog 的三种格式?
- redo log 容灾恢复过程?
- redo log 是直接写入磁盘的吗?
- redo log 先写入 buffer 再落盘与直接落盘相比,有什么优点?
- redo log buffer 什么时候刷盘?
- redo log 文件格式和写入过程?
- 文件格式
- 物理结构
- 逻辑结构
- LSN(Log Sequence Number)
- 写入过程
- 事务提交阶段
- 日志写入流程
- Checkpoint 机制
- 崩溃恢复
- 关键设计思想
MySQL 三种日志?
【在 csview 上这条八股题目被命名为 “MySQL 三种日志?”,但 DeepSeek 给出的答案我个人认为更加完整,csview 中总结的 undo log、redo log 和 bin log 应该被称为 MySQL 的三个核心日志,此外还有error log、slow query log 和 general query log 等辅助日志】
首先总结三种核心日志。
二进制日志(Binlog)
作用:记录所有修改数据库的 SQL 语句(逻辑日志),用于主从复制和数据恢复。
特点:属于 MySQL Server 层的日志,与存储引擎无关。
重做日志(Redo Log)
作用:确保事务的持久性,主要用于掉电等故障恢复。记录物理页的修改,用于崩溃恢复时重放未持久化到磁盘的操作。
特点:属于 InnoDB 引擎层日志,采用循环写入模式。
回滚日志(Undo Log)
作用:提供事务回滚能力(原子性)。支持 MVCC(多版本并发控制),实现非锁定读。
特点:属于 InnoDB 引擎层日志,记录事务前的数据旧版本。事务提交后不会立即删除,可能被其他事务的 MVCC 读取依赖。
其它辅助日志
- 错误日志(Error Log):记录 MySQL 启动、运行、停止时的错误信息;
- 慢查询日志(Slow Query Log):记录执行时间超过阈值的 SQL,用于性能优化;
- 通用查询日志(General Query Log):记录所有客户端请求(生产环境慎用);
总结
Binlog 用于数据恢复与数据复制;Redo Log 保障崩溃恢复(比如掉电恢复);Undo Log 用于事务回滚与 MVCC。
redo log 与 undo log 的区别?
redo log 记录的是此次事务完成后的数据状态,undo log 记录的是此次事务开始前的数据状态。
undo log 是如何实现 MVCC 的?
对于读提交和可重复读隔离级别,快照读是通过 Read View + undo log 来实现的。这两种隔离级别下快照读的主要区别是建立 Read View 的时机不同:
- 读提交(Read Committed)在每个 select 语句执行时都会生成一个新的 Read View,事务执行期间如果多次读取同一条数据,则前后两次读的数据可能会出现不一致(原因在于,在读提交隔离级别下,一个事务提交之后,其所做的更改会被其它事务看到,即不能保证可重复读),因为在此期间可能另一个事务修改了这条记录,并提交了事务。
- 可重复读隔离级别是在启动事务时生成一个 Read View,然后整个事务期间,所有的 select 语句共用一个 Read View,保证事务期间读到的数据都是事务启动前的记录。
通过对比事务的 Read View 当中字段和记录的隐藏列 trix_id 和 roll_pointer,如果不满足版本可见行,就会顺着 undo log 版本链找到满足可见性的记录,从而控制并发事务访问同一个记录时的行为。
redo log 与 binlog 的区别?
- 架构分层差异:binlog 在 MySQL 的 Server 层实现,所有存储引擎都可以用;而 redo log 是 InnoDB 存储引擎实现的日志;
- 文件格式不同:binlog 是逻辑日志,记录原始 SQL 语句或行变更逻辑;redo log 是物理日志,记录的是在某个数据页做了什么样的修改。物理日志恢复效率更高(直接应用物理修改),逻辑日志更通用但是恢复速度慢;
- 写入方式不同:binlog 是追加写,写满一个文件,就会创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志;redo log 是循环写,日志空间大小固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志。循环写入更适合高频事务场景,避免日志文件无限膨胀;
- 用途不同:binlog 用于数据备份和数据恢复;redo log 用于故障恢复;
为什么有 binlog,还需要有 redo log?
早期版本的 MySQL 以 MyISAM 作为默认的数据引擎,它没有 crash-safe 的能力,binlog 日志仅用于归档。InnoDB 数据引擎可以通过 redo-log 实现 crash-safe 能力。
什么是 crash-safe?
基于 redo log 和 WAL 技术,InnoDB 可以保证即使数据库异常重启,之前已经提交的数据也不会丢失。
什么是 WAL 技术?
WAL 指的是,MySQL 的写操作不是立刻写到磁盘上,而是先写日志,然后在合适的时间写到磁盘。
被修改的 undo 页面,需要记录对应的 redo log 吗?
需要。开启事务后,InnoDB 更新记录前,首先要记录相应的 undo log,如果是更新操作,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面。在内存修改该 Undo 页面后,需要记录对应的 redo log。
binlog 的三种格式?
分别是 STATEMENT(默认格式)、ROW、MIXED;
- STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中,主从复制从 slave 端根据 SQL 语句重现;
- ROW:记录行数据最终被修改成什么样了,不会出现 STATEMENT 动态函数问题;
- MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
redo log 容灾恢复过程?
如果 redo log 是完整的(commit 状态),那么直接使用 redo log 恢复;
如果 redo log 是预提交 prepare 而不是 commit 状态,此时要去判断 binlog 是否完整,完整就提交 redo log,再用 redo log 恢复,不完整就回滚事务。
redo log 是直接写入磁盘的吗?
不是。直接写入磁盘会产生大量 I/O,redo log 会先写入 redo log buffer,每当产生一条 redo log,先写入 buffer,后续再持久化到磁盘。
以上是 csview 整理的答案,而根据 DeepSeek 给出的回答,实际上 redo log 是否直接落盘取决于 MySQL 的配置策略,由参数innodb_flush_log_at_trx_commit
控制。
innodb_flush_log_at_trx_commit = 1
时,每次事务提交,log buffer 中的 redo log 都会立即直接写入操作系统缓存并调用fsync
刷盘,确保事务提交后数据不丢失,但会降低吞吐量;innodb_flush_log_at_trx_commit = 0
时,log buffer 每秒写入 OS 缓存并刷盘一次,事务提交时不主动触发写盘。风险是最多都是 1 秒的事务数据;innodb_flush_log_at_trx_commit = 2
时,事务提交时 log buffer 写入 OS 缓存,但每秒调用fsync
落盘一次。风险是 OS 崩溃时丢失 1 秒数据,硬件故障也可能丢失。
redo log 先写入 buffer 再落盘与直接落盘相比,有什么优点?
redo log 的写方式使用了追加,日志操作是顺序写,磁盘操作是随机写,MySQL 的写操作从磁盘的随机写变成了顺序写,提升语句的执行性能。
- 实现事务的持久性,让 MySQL 有 crash-safe 的能力,能够保证 MySQL 在任何时间段突然崩溃,重启后之前已提交的记录都不会丢失;
- 将写操作从随机写变成了顺序写,提升 MySQL 写入磁盘的性能。
redo log buffer 什么时候刷盘?
- MySQL 正常关闭时触发落盘;
- 当 redo log buffer 中记录的数据大于 redo log buffer 内存空间的一半时,触发落盘;
- InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘;
- 每次事务提交时都将缓存在 redo log buffer 里的 redo log 持久化到磁盘。
redo log 文件格式和写入过程?
文件格式
物理结构
由 ib_logfile0
、ib_logfile1
等文件组成(默认2个,可配置)。每个文件大小固定(如48MB),以循环写入(Circular Buffer)方式复用。
逻辑结构
日志块(Log Block):每个块512字节(与磁盘扇区对齐);
日志记录(Log Record):每个事务的修改操作记录。包含表空间ID、页号、修改内容等元数据。
LSN(Log Sequence Number)
全局递增的唯一序列号,标记日志写入位置,用于崩溃恢复时定位数据一致性点。
写入过程
事务提交阶段
修改操作先写入内存中的 redo log buffer,根据 innodb_flush_log_at_trx_commit
(0 / 1 / 2)配置决定刷盘策略。
日志写入流程
将 redo log buffer 中的日志按 LSN 顺序写入文件。写入时追加到当前活跃的日志文件(如ib_logfile0)。若文件写满,切换到下一个文件(循环覆盖旧日志)
Checkpoint 机制
- 定期将脏页(修改后的数据页)刷入磁盘;
- 更新Checkpoint LSN,标记已持久化的日志位置;
- 释放已无用的日志空间(避免无限增长);
崩溃恢复
- 重启时比较 Checkpoint LSN 和最新 LSN;
- 重放 Checkpoint 之后的所有日志记录;
- 确保未刷盘的脏页修改被恢复;
关键设计思想
- 顺序写入:日志追加写性能远高于随机写数据文件;
- WAL:先写日志后写数据;
- 幂等性:日志记录包含足够信息,可重复执行恢复。