MySQL事务日志类型及作用解析
undolog、redolog 和 binlog 是 InnoDB 存储引擎和 MySQL Server 层协同工作以实现事务、崩溃恢复和数据复制等关键功能的核心组件。
下面我将详细解释它们的作用和区别。
总结概览
特性 | Redo Log (重做日志) | Undo Log (回滚日志) | Binlog (二进制日志) |
---|---|---|---|
所属层级 | InnoDB 存储引擎层 | InnoDB 存储引擎层 | MySQL Server 层 |
日志类型 | 物理日志 | 逻辑日志 | 逻辑日志 (或 Statement/Row 格式) |
主要作用 | 崩溃恢复 (Crash Recovery) | 事务回滚 (Rollback) 和 MVCC | 数据复制 (Replication) 和 数据恢复 |
内容记录 | 记录的是“在某个数据页上做了什么物理修改” | 记录的是数据被修改前的逻辑状态 | 记录的是修改数据的SQL 语句 (Statement) 或行变化 (Row) |
生命周期 | 循环写入,空间会被覆盖 | 随事务结束而失效,由 Purge 线程清理 | 追加写入,文件会增长,可手动清理 |
持久化时机 | Write-Ahead Logging (WAL),先写日志再写磁盘 | 在事务执行过程中写入 | 在事务提交后一次性写入 (可通过配置调整) |
用途 | 保证事务的持久性 (Durability) | 保证事务的原子性 (Atomicity) 和 隔离性 (Isolation) | 主从复制、数据备份与恢复 |
详细解析
1. Redo Log (重做日志)
作用: 保证事务的持久性。 即使数据库发生宕机,重启后也能恢复已经提交但尚未写入数据文件的事务操作,防止数据丢失。
工作原理:
WAL 机制 (Write-Ahead Logging): 当发生数据修改时,InnoDB 并不会立刻将数据页(Data Page)刷新到磁盘上(因为这是随机IO,很慢),而是会先将这次修改记录到 redo log buffer 中。
顺序写入: redo log buffer 会按照一定策略(如每秒、每个事务提交时)顺序写入到磁盘上的 redo log 文件(通常是
ib_logfile0
,ib_logfile1
)。顺序IO的速度远快于随机IO,这是 InnoDB 高性能的重要原因。崩溃恢复: 当数据库正常运行时,后台线程会逐步将脏页(修改过的数据页)刷新到磁盘。如果数据库在脏页刷盘前崩溃,重启后 InnoDB 会读取 redo log,重做(redo)所有已经提交但未落盘的操作,从而将数据库恢复到崩溃前的状态。
简单比喻: 就像小饭店的老板娘记账。客人点菜后,她先飞快地记在小本子(redo log)上,然后后厨才开始做菜(修改数据页)。即使后厨忘了某个菜,只要查一下小本子就知道漏了啥。小本子是循环用的,记满后就擦掉前面重新记。
2. Undo Log (回滚日志)
作用:
实现事务回滚 (原子性): 当你执行
ROLLBACK
语句时,InnoDB 会利用 undo log 将数据恢复到修改前的状态。实现 MVCC (多版本并发控制) (隔离性): 这是它的另一个关键作用。当某个事务正在修改数据而另一个事务需要读取该数据时,InnoDB 会通过 undo log 构建该数据的旧版本快照,提供给读事务使用,从而实现非阻塞读(Read View 机制)。
工作原理:
在事务修改数据之前,会先将数据的原始版本逻辑地复制到 undo log 中。
undo log 也会被持久化到磁盘上的 undo 表空间(MySQL 8.0 之前是 ibdata 文件,8.0 之后是独立的
.ibu
文件)。回滚时,就根据 undo log 里的记录进行逆向操作(例如,insert 的逆向是 delete,update 的逆向是 update 回旧值)。
生命周期: 当事务提交后,对应的 undo log 不会立刻删除,因为它可能还被其他运行中的事务用于 MVCC。只有当系统里没有比这个 undo log 更早的 Read View 时,它才会被后台的 Purge 线程清理掉。
3. Binlog (二进制日志)
作用:
主从复制 (Replication): 这是其最核心的功能。Master 节点将 binlog 发送给 Slave 节点,Slave 节点重放这些日志,从而实现数据同步。
数据恢复 (Point-in-Time Recovery): 你可以使用
mysqlbinlog
工具解析 binlog,恢复某个时间点的数据。例如,每周一次全量备份,然后通过重放 binlog 将数据恢复到周三凌晨的某个状态。
与 redo log 的区别:
层级不同: binlog 是 MySQL Server 层的,所有存储引擎都可以使用;redo log 是 InnoDB 独有的。
内容不同: binlog 是逻辑日志,记录的是原始的 SQL 语句(Statement 格式)或者行变化(Row 格式);redo log 是物理日志,记录的是对哪个数据页的哪个偏移量做了什么修改。
写入方式不同: binlog 是追加写入,写满一个文件就换下一个,不会覆盖;redo log 是循环写入,空间固定。
写入时机不同: 在默认的
sync_binlog=1
和innodb_flush_log_at_trx_commit=1
配置下,事务提交时,redo log 和 binlog 都需要持久化到磁盘。这引出了著名的两阶段提交 (2PC) 协议,以确保这两个日志的逻辑一致性,这是实现崩溃恢复后数据一致性的关键。
协同工作流程:两阶段提交 (2PC)
以一个简单的 UPDATE
语句为例:
执行器通知 InnoDB 更新数据。
InnoDB 找到数据,加载数据页到缓冲池,记录 undo log。
InnoDB 更新缓冲池中的数据,记录 redo log (处于 prepare 状态)。
执行器生成该操作的 binlog,并将其写入磁盘。
执行器调用 InnoDB 的提交接口。
InnoDB 将刚才写入的 redo log 状态从 prepare 改为 commit。
事务提交完成。
为什么需要两阶段提交?
为了防止在崩溃恢复时出现数据不一致。如果只有一步,可能会出现:
先写 redo log 后写 binlog: redo log 提交了,但 binlog 没写。主库数据是新值,但从库通过 binlog 同步的是旧值,主从不一致。
先写 binlog 后写 redo log: binlog 写了,但 redo log 没提交。主库崩溃恢复后,事务被回滚(数据是旧值),但从库已经同步了 binlog(数据是新值),主从不一致。
两阶段提交的崩溃恢复:
数据库重启后,会检查 redo log 和 binlog:
如果 redo log 里的事务是 commit 状态,直接提交。
如果 redo log 里的事务是 prepare 状态,则去检查 binlog:
如果 binlog 是完整的(该事务的日志存在),则提交事务。
如果 binlog 是不完整的(该事务的日志不存在),则回滚事务。
这样就保证了只要 binlog 写了,事务就一定提交;binlog 没写,事务就一定回滚,从而确保了主从数据的一致性。