Undo Log(撤销日志)详解
首先介绍的是 Undo Log,从字面理解,它主要用于“撤销”或“取消”操作,目标是将数据回滚到某个指定的状态。这是一种逻辑日志,详细记录了数据变换的过程。在 InnoDB 存储引擎中,Undo Log 以段(segment)的形式进行管理和记录。具体来说,InnoDB 的数据文件中包含一个名为回滚段(rollback segment)的结构,其中封装了 1024 个 Undo Log 段。你可以通过参数 innodb_undo 来灵活控制其行为。
show variables like '%innodb_undo%';
Undo Log 在事务启动之前就会生成。值得留意的是,当事务提交后,它并不会立即被删除。InnoDB 会先将该事务对应的 Undo Log 放入一个删除列表,然后由后台的 purge thread 线程逐步回收处理。举个例子,假设执行了一条 DELETE 操作,Undo Log 就会记录一条对应的 INSERT 操作;如果执行了 UPDATE,它则会记录一个相反的 UPDATE 操作。
Undo Log 的核心作用
保障事务的原子性
可以说,Undo Log 就是为事务原子性而设计的。在事务处理过程中,如果发生错误,或者用户主动执行 ROLLBACK,MySQL 就可以借助 Undo Log 中保存的备份数据,将数据库恢复到事务开始之前的状态。
辅助实现多版本并发控制(MVCC)
在 MySQL InnoDB 引擎中,Undo Log 也是实现多版本并发控制(MVCC)的关键组件。事务尚未提交时,Undo Log 保存的是数据未提交之前的版本。这些旧版本数据可以作为快照,供其他并发事务进行快照读操作。
来看一个具体的示例:

图中演示了这样一个场景:事务 A 手动开启事务,执行了一个更新操作。它会先将待更新的数据备份到 Undo Buffer 中。此时,事务 B 也手动开启事务,执行了一个查询操作。事务 B 会读取 Undo Buffer 中的日志数据并返回,这就是一次典型的快照读操作。
Redo Log 与 Binlog 对比解析
Redo Log 和 Binlog 是 MySQL 日志体系中两个至关重要的角色,但它们的职责和特性差异很大。
Redo Log(重做日志)
顾名思义,它用于“重做”操作,主要是在数据库意外崩溃时进行数据恢复。
Redo Log 会随着事务操作的执行而实时生成。在事务提交时,产生的 Redo Log 会先被写入到名为 Log Buffer 的内存区域,而非立刻刷入磁盘文件。直到事务的脏页被真正写入磁盘后,Redo Log 的使命才算完成,其所占的空间也可被重用(覆盖写入)。
Redo Log 的工作流程示意图

Redo Log 的写入机制
Redo Log 文件采用循环写入方式,即写满最后一个文件后,会回溯到第一个文件,从头开始覆盖写入。

通过这张图可以看得很明白:
- Write Pos 是当前记录的写入位置,它会一边写入一边向后移动,写到最后一个文件末尾后,再回到 0 号文件的开头。
- Check Point 是当前要擦除的位置,同样也是向后移动并循环。在擦除记录之前,必须先将记录更新到数据文件中。
Write Pos 与 Check Point 之间的空白区域,是可以记录新操作的空间。如果 Write Pos 追上了 Check Point,说明空间已写满,此时不能再执行新的更新操作,必须停下来擦除部分旧记录,将 Check Point 向前推进。
Redo Log 的相关配置参数
每个 InnoDB 引擎至少有一个重做日志文件组(Redo Log Group),每个文件组至少包含 2 个重做日志文件(Redo Log),默认文件名为 ib_logfile0 和 ib_logfile1。关于 Redo Buffer 持久化到 Redo Log 的策略,可以通过参数 Innodb_flush_log_at_trx_commit 来设置:
Innodb_flush_log_at_trx_commit =0:
- 表示每秒进行一次 Redo Buffer → OS Cache → flush cache to disk 的完整操作。这种方式可能会丢失 1 秒内的事务数据,因为后台的 Master 线程每隔 1 秒才执行一次。
Innodb_flush_log_at_trx_commit =1:
- 每次事务提交时,立即执行 Redo Buffer → OS Cache → flush cache to disk。这是最安全的方式,但性能也是最差的。
Innodb_flush_log_at_trx_commit =2:
- 每次事务提交时,只执行 Redo Buffer → OS Cache。然后由后台的 Master 线程每隔 1 秒执行 OS Cache → flush cache to disk 的操作。
通常推荐取值 2。这样 MySQL 进程崩溃不会丢失数据,只有整个操作系统宕机,才会损失 1 秒内的事务提交数据。
流程图如下:

Binlog(二进制日志)详解
Binlog 的记录模式
Redo Log 是 InnoDB 引擎特有的日志,而 MySQL Server 自身也拥有一套日志——Binary Log(二进制日志),通常简称为 Binlog。
Binlog 以事件形式记录了所有数据库表结构的变更以及表数据的修改操作,同时包含语句执行所耗的时间。但需要说明的是,它不会记录 SELECT 和 SHOW 这类操作。开启 Binlog 主要服务于两个场景:
- 主从复制:在主库上启用 Binlog,主库将 Binlog 内容传输给从库,从库收到后重放,从而保证主从数据的一致性。
- 数据恢复:通过 mysqlbinlog 工具来恢复数据。
Binlog 的文件名默认格式为“主机名_binlog-序列号”,例如 oak_binlog-000001,也可以在配置文件中自定义。其记录模式有三种:STATEMENT、ROW 和 MIXED。具体含义如下:
ROW(基于行的复制,简称 RBR):日志中会记录每一行数据被修改的具体情况,从库针对相同数据进行修改。
- 优点:能清晰记录每个行数据的修改细节,完全实现主从数据同步和数据恢复。
- 缺点:批量操作时会产生大量日志,尤其是 ALTER TABLE 这类操作,容易导致日志暴涨。
STATEMENT(基于语句的复制,简称 SBR):每一条修改数据的 SQL 语句都会被记录到主库的 Binlog 中。从库在复制时,SQL 进程解析这些 SQL 并在从库上重新执行,本质上是 SQL 语句的复制。
- 优点:日志量小,减少磁盘 I/O,提升存储和恢复速度。
- 缺点:某些情况下会导致主从数据不一致,例如使用了 last_insert_id()、now() 等函数。
MIXED(混合模式,简称 MBR):通常使用 STATEMENT 模式保存 Binlog,但对于 STATEMENT 模式无法正确复制的操作,会自动切换到 ROW 模式。MySQL 根据执行的 SQL 语句智能选择相应模式。
Binlog 文件的结构
MySQL 的 Binlog 文件中记录了所有对数据库的修改操作,用来表示这些修改操作的数据结构称为 Log Event。不同的修改操作对应不同的 Log Event,常见的有 Query Event、Row Event、Xid Event 等。可以理解为 Binlog 文件内容就是各种 Log Event 的集合。
下面展示 Binlog 文件中 Log Event 的结构:

Binlog 的写入机制
- 首先,根据记录模式和操作类型触发 event 事件,生成相应的 Log Event(即事件触发执行机制)。
- 然后,事务执行过程中产生的 Log Event 会先写入一个缓冲区,每个事务线程都有自己的缓冲区。Log Event 保存在名为 binlog_cache_mngr 的数据结构中,该结构包含两个缓冲区:stmt_cache 用于存放不支持事务的消息,trx_cache 用于存放支持事务的消息。
事务在提交阶段,会将产生的 Log Event 写入到外部的 Binlog 文件中。不同事务以串行方式写入,因此一个事务包含的 Log Event 信息在 Binlog 文件中是连续存储的,中间不会插入其他事务的 Log Event。
Binlog 文件操作相关命令
查看 Binlog 状态
show variables like 'log_bin';
开启 Binlog 功能
set global log_bin=mysqllogbin;(注意:此命令可能报错:ERROR 1238 (HY000): Variable 'log_bin' is a read only variable,此时需要修改 my.cnf 或 my.ini 配置文件,在 [mysqld] 下增加 log_bin=mysql_bin_log,然后重启 MySQL 服务。)
#log-bin=ON #log-bin-basename=mysqlbinlog binlog-format=ROW log-bin=mysqlbinlog
使用 show binlog events 命令
show binary logs;// 等价于 show master logs; show master status; show binlog events; show binlog events in 'mysqlbinlog.000001';
使用 mysqlbinlog 命令
mysqlbinlog "文件名" mysqlbinlog "文件名" > "test.sql"
使用 binlog 恢复数据
-- 按时间恢复 mysqlbinlog --start-datetime="2020-0425 18:00:00" --stop-datetime="2020-04-26 00:00:00" mysqlbinlog.000002 | mysql -uroot -p1234 -- 按事件位置号恢复 mysqlbinlog --start-position=154 --stop-position=957 mysqlbinlog.000002 | mysql -uroot -p1234
通常,mysqldump 用于定期全量备份数据库,而 mysqlbinlog 则用于增量备份和恢复操作。
删除 Binlog 文件
-- 删除指定文件 purge binary logs to 'mysqlbinlog.000001'; -- 删除指定时间之前的文件 purge binary logs before '2020-0428 00:00:00'; -- 清除所有文件 reset master;
也可以通过设置 expire_logs_days 参数进行自动清理。默认值为 0(未启用),若设置为 1,表示 Binlog 文件超过 1 天将自动被删除。
最后,总结一下 Redo Log 和 Binlog 的区别:
Redo Log 是 InnoDB 引擎独有的,而 Binlog 是 MySQL Server 自带的,以二进制文件形式记录。
Redo Log 属于物理日志,记录的是数据页更新后的状态;而 Binlog 是逻辑日志,记录的是更新的过程。
Redo Log 采用循环写入方式,日志空间大小固定;而 Binlog 是追加写入,写完一个文件继续写下一个,不会覆盖。
最关键的是,Redo Log 用于服务器异常宕机后事务数据的自动恢复;而 Binlog 主要用于主从复制和数据恢复。坦率地说,Binlog 本身不具备自动的 crash-safe 能力。
