在 MySQL 的面试中,关于日志系统的问题几乎是必考项,尤其是 redo log、undo log 和 binlog 这三者之间的区别与协作流程。很多候选人对单个日志的概念能说出一二,但一旦被问到“一次更新语句执行时,这三个日志是如何配合工作的”,往往就答不完整。本文将从原理出发,系统梳理这三种日志的定位、写入机制以及它们在一次事务提交中的协作流程。
一、三种日志的定位与核心区别
在深入协作流程之前,先明确每种日志“为什么存在”。
1. redo log(重做日志)
redo log 是 InnoDB 存储引擎特有的物理日志,记录的是“在某个数据页上做了什么修改”。它的核心作用是保证事务的持久性,同时通过 WAL(Write-Ahead Logging)机制提升写入性能。
- 所属层:InnoDB 存储引擎层。
- 日志类型:物理日志,记录数据页的物理变更。
- 写入方式:循环写,固定大小(由
innodb_log_file_size和innodb_log_files_in_group控制),写满后覆盖最旧的部分。 - 核心作用:崩溃恢复。即使脏页还没刷盘,只要 redo log 落盘,重启后就能恢复数据。
2. undo log(回滚日志)
undo log 同样属于 InnoDB 引擎层,记录的是数据的“逻辑反向操作”。比如执行一条 INSERT,undo log 中会记录一条对应的 DELETE;执行 UPDATE,会记录反向的 UPDATE。
- 所属层:InnoDB 存储引擎层。
- 日志类型:逻辑日志,记录反向操作。
- 写入方式:随事务产生,存储在 undo 表空间中。
- 核心作用:事务回滚和 MVCC(多版本并发控制)。回滚时执行反向操作;MVCC 中通过 undo log 找到数据的历史版本,实现一致性读。
3. binlog(二进制日志)
binlog 是 MySQL Server 层实现的逻辑日志,记录的是“数据库执行的所有修改操作”,比如一条 UPDATE 语句或一行数据的变化。
- 所属层:MySQL Server 层,所有存储引擎共用。
- 日志类型:逻辑日志,记录 SQL 语句或行变更。
- 写入方式:追加写,文件写满后切换到新文件,不会覆盖。
- 核心作用:主从复制和数据恢复(如 point-in-time recovery)。
三者核心区别对比
| 维度 | redo log | undo log | binlog |
|---|---|---|---|
| 所属层 | InnoDB 引擎层 | InnoDB 引擎层 | Server 层 |
| 日志类型 | 物理日志 | 逻辑日志(反向) | 逻辑日志 |
| 写入方式 | 循环写 | 随事务写 | 追加写 |
| 主要作用 | 持久性、崩溃恢复 | 回滚、MVCC | 主从复制、归档恢复 |
| 是否可关闭 | 不可 | 不可 | 可(skip-log-bin) |
二、两阶段提交与协作流程
理解了三种日志的定位后,最关键的问题是:一次 UPDATE 语句执行时,它们是如何协作的?
这里涉及 MySQL 保证数据一致性的核心机制——两阶段提交(2PC)。之所以需要两阶段提交,是因为 redo log 和 binlog 分属不同层,如果两者写入不一致,会导致主从数据不一致或崩溃恢复后数据错乱。
一次 UPDATE 语句的完整执行流程
假设执行 UPDATE user SET name = 'Tom' WHERE id = 1;,流程如下:
第一阶段:准备阶段(Prepare)
- 执行器调用 InnoDB 引擎接口,获取 id=1 这一行数据。如果数据页在内存中就直接返回,否则从磁盘读入内存。
- 写入 undo log:记录修改前的旧值,用于回滚和 MVCC。
- 更新内存中的数据页:将 name 改为 'Tom',此时数据页成为脏页。
- 写入 redo log:将修改记录到 redo log buffer,此时 redo log 处于 prepare 状态。
- 写入 binlog:执行器将修改操作写入 binlog,并刷盘(受
sync_binlog控制)。
第二阶段:提交阶段(Commit)
- 提交事务:redo log 的状态从 prepare 改为 commit。
- 后台刷脏:InnoDB 的后台线程在合适时机将脏页刷入磁盘。
为什么需要两阶段提交?
关键在于 redo log 的 prepare 和 commit 两个状态。假设不用两阶段提交,而是先写 redo log 再写 binlog,或者反过来,会出现什么问题?
场景一:先写 redo log,再写 binlog,binlog 写入前崩溃。
- 重启后,redo log 已落盘,InnoDB 认为事务已提交,数据恢复为新值。
- 但 binlog 没有记录这条修改,从库不会同步这条数据。
- 结果:主库和从库数据不一致。
场景二:先写 binlog,再写 redo log,redo log 写入前崩溃。
- 重启后,redo log 没有记录,InnoDB 认为事务未提交,回滚。
- 但 binlog 已经写入,从库会同步这条修改。
- 结果:主库回滚了,从库却执行了,数据不一致。
两阶段提交的解决方式:
- 崩溃恢复时,如果 redo log 处于 prepare 状态,则去检查对应的 binlog 是否完整。
- 如果 binlog 完整,则提交事务(redo log 改为 commit)。
- 如果 binlog 不完整,则回滚事务。
- 如果 redo log 已经是 commit 状态,则直接提交。
这样就保证了 redo log 和 binlog 的逻辑一致性,无论在哪一步崩溃,主从数据都能保持一致。
三、常见面试追问
追问 1:redo log 和 binlog 都能恢复数据,有什么区别?
redo log 是物理日志,循环写,用于崩溃恢复,保证的是“已提交事务不丢失”;binlog 是逻辑日志,追加写,用于归档和主从复制,可以恢复到任意时间点。redo log 空间有限,binlog 可以长期保存。
追问 2:undo log 什么时候删除?
undo log 在事务提交后不会立即删除。因为 MVCC 可能需要它来提供历史版本,只有当没有更早的 Read View 需要用到该 undo log 时,才会由 purge 线程清理。
追问 3:为什么 redo log 要采用循环写?
因为 redo log 的核心目标是崩溃恢复,只需要保证“脏页刷盘前,对应的 redo log 还在”即可。一旦脏页刷盘,对应的 redo log 就可以被覆盖。循环写节省空间,同时顺序写入性能高。
追问 4:binlog 的三种格式有什么区别?
- STATEMENT:记录 SQL 语句,日志量小,但某些函数(如 NOW())可能导致主从不一致。
- ROW:记录每行数据的变更,日志量大,但主从一致性强,是当前推荐格式。
- MIXED:混合模式,普通语句用 STATEMENT,可能不一致的用 ROW。
四、总结
redo log、undo log 和 binlog 是 MySQL 保证数据可靠性、一致性和可恢复性的三驾马车:
- redo log 保证持久性,让已提交的事务不丢失;
- undo log 保证原子性和 MVCC,支持回滚和一致性读;
- binlog 保证可复制性和归档恢复,支撑主从架构。
而两阶段提交是连接 redo log 和 binlog 的桥梁,通过 prepare 和 commit 两个阶段,确保两者逻辑一致,从而保证主从数据一致和崩溃恢复的正确性。理解这套协作流程,不仅能在面试中脱颖而出,也能在实际排查数据不一致问题时提供清晰的思路。
未经允许不得转载:任鹏个人博客 » MySQL 中 redo log、undo log、binlog 的区别与协作流程

