MySQL:Redo Log、Binlog 与 Undo Log 原理深入剖析
1、简述
在理解具体实现之前,我们先回答一个关键问题:为什么MySQL需要三种不同的日志?这要从MySQL的架构和数据安全需求说起。
MySQL采用插件式存储引擎架构,Server层负责SQL解析、优化和执行,存储引擎层(最常用的是InnoDB)负责数据的实际存储和事务管理。这种分层架构直接决定了日志的职责划分:
- Redo Log:InnoDB层特有,解决“内存与磁盘速度不匹配”的性能痛点,保障事务持久性(Durability)
- Undo Log:InnoDB层特有,实现事务原子性(Atomicity)和MVCC多版本并发控制
- Binlog:Server层通用,服务于主从复制和数据恢复,所有存储引擎共享
一句话总结:Redo Log让数据库“不死”,Undo Log让事务“可悔”,Binlog让数据“可复制”。

2、Redo Log:崩溃恢复的“救命稻草”
2.1 设计初衷:解决Buffer Pool的“后顾之忧”
InnoDB为了提升性能,引入了内存中的Buffer Pool。所有读写操作优先在Buffer Pool中进行,数据页被修改后成为“脏页”,并不会立即刷回磁盘(磁盘随机写入代价极高)。但内存是易失的——如果事务提交后、脏页刷盘前发生宕机,数据就会永久丢失。
Redo Log正是为了解决这个矛盾而生:事务提交时,先把数据页的修改操作以顺序写入的方式记录到Redo Log并持久化,再异步将脏页刷盘。
2.2 核心特性:物理日志 + 循环写 + WAL
| 特性 | 说明 | 设计意图 |
|---|---|---|
| 物理日志 | 记录“哪个数据页(空间ID+页号)的哪个偏移量,做了什么修改” | 体积小、写入快,恢复时无需重新执行SQL |
| 循环写 | 由多个固定大小文件组成(innodb_log_file_size控制大小),写满后从头覆盖 |
固定空间占用,避免无限增长 |
| WAL原则 | 所有修改必须先写日志,再在合适时机刷盘 | 保证持久性的核心机制 |
WAL(Write-Ahead Logging) 是Redo Log的基石:事务提交时,Redo Log必须先于数据页写入磁盘。InnoDB内部以**mini-transaction(mtr)**为单位,一个mtr可能覆盖多个数据页的修改,在崩溃恢复时要么全部恢复,要么全部不恢复,保证了数据一致性。
2.3 刷盘策略:安全与性能的权衡
innodb_flush_log_at_trx_commit参数控制事务提交时Redo Log的刷盘行为:
| 参数值 | 行为 | 安全性 | 性能 | 适用场景 |
|---|---|---|---|---|
| 1(默认) | 事务提交时立即刷盘 | 最高,宕机不丢数据 | 最低 | 金融、订单等核心业务 |
| 0 | 每秒刷一次,事务提交时不刷 | 最低,宕机丢1秒数据 | 最高 | 可容忍数据丢失的缓存场景 |
| 2 | 写入OS缓存,由操作系统每秒刷盘 | 中等,宕机丢OS缓存数据 | 较高 | 大部分常规业务 |
2.4 崩溃恢复流程
数据库重启时,InnoDB自动执行崩溃恢复:
- 扫描Redo Log文件,找到所有已提交但未刷盘的事务
- 按照Redo Log的记录重放数据页的修改操作
- 未提交的事务由Undo Log负责回滚
Recovery过程中,只恢复完整的mtr日志组——如果某个mtr的日志不完整(说明写日志时发生了崩溃),则该组所有修改都被丢弃,不会破坏数据结构。
3、Undo Log:事务回滚的“后悔药”
3.1 核心作用:撤销修改 + MVCC基石
Undo Log记录的是事务修改数据之前的原始状态,承担两大职责:
- 事务回滚:事务执行中出错或主动ROLLBACK时,通过Undo Log恢复数据到事务开始前的状态,保障原子性
- MVCC多版本并发控制:Undo Log中保存了数据行的历史版本链(通过
roll_pointer指针串联),配合Read View实现无锁快照读
3.2 日志类型与清理机制
InnoDB中Undo Log分为两种类型:
| 类型 | 产生时机 | 清理时机 |
|---|---|---|
| insert undo log | INSERT操作 | 事务提交后立即删除(新插入的记录对其他事务不可见) |
| update undo log | UPDATE或DELETE操作 | 事务提交后不能立即删除,需等待所有快照读不再需要该版本,由后台Purge线程异步回收 |
3.3 版本链与MVCC
一条记录每次被修改时,会在Undo Log中生成一个新版本,通过roll_pointer串联成版本链,每个版本还记录了产生它的事务ID(trx_id)。
当并发事务执行快照读(普通SELECT)时,InnoDB根据Read View(一致性视图)决定可见哪个版本。这是InnoDB实现**RC(读已提交)和RR(可重复读)**隔离级别的核心机制,实现了“读不阻塞写,写不阻塞读”的高并发能力。
3.4 长事务的隐患
由于Undo Log在事务提交后不会立即清理(尤其是update undo log),长事务会导致Undo Log急剧膨胀,占用大量磁盘空间,同时版本链过长导致查询性能下降。生产环境应监控information_schema.innodb_trx,避免事务长时间未提交。
4、Binlog:主从复制的“数据桥梁”
4.1 Binlog的定位
与Redo/Undo Log不同,Binlog是Server层的日志,不依赖于具体存储引擎。它记录所有DDL(创建库/表)和DML(增删改)操作,但不记录SELECT/SHOW等查询语句。
三大核心用途:
- 主从复制:Master将Binlog发送给Slave,Slave重放实现数据同步
- 时间点恢复:通过全量备份 + Binlog增量,恢复到任意时间点
- 审计:记录数据库变更历史
4.2 三种日志格式
binlog_format参数控制记录方式:
| 格式 | 记录内容 | 优点 | 缺点 |
|---|---|---|---|
| STATEMENT | SQL语句本身 | 日志量小,节省空间和IO | 非确定性函数(如NOW())可能导致主从不一致 |
| ROW(默认) | 每一行数据的变化(修改前后镜像) | 数据安全,主从绝对一致 | 日志量大,尤其是批量操作 |
| MIXED | 自动切换:默认STATEMENT,不安全时切换为ROW | 平衡性能和安全性 | 逻辑复杂 |
推荐使用ROW格式:虽然日志量更大,但在数据恢复时可以直接从Binlog中提取修改前的数据(DELETE改INSERT,UPDATE改反向UPDATE),是误操作后的救命稻草。
4.3 写入机制与刷盘策略
Binlog采用两阶段写入:事务执行过程中,日志先写入线程私有的binlog cache;事务提交时,再将binlog cache一次性写入磁盘文件。
sync_binlog参数控制刷盘时机:
| 参数值 | 行为 | 安全性 |
|---|---|---|
| 0 | 每次提交只write,由OS决定fsync | 宕机可能丢失binlog |
| 1(推荐) | 每次提交都执行fsync | 最高,不丢binlog |
| N | 每N次提交执行一次fsync | 折中方案 |
5、三大日志协同工作:两阶段提交
5.1 为什么需要两阶段提交?
以一条UPDATE语句为例:事务提交时,Redo Log(InnoDB层)和Binlog(Server层)都需要写入。如果先写Redo Log再写Binlog,写完Redo Log后宕机,重启后Redo Log恢复了数据,但Binlog中没有该操作,导致主从数据不一致。反之亦然。
两阶段提交(2PC)解决了这个分布式一致性问题。
5.2 完整执行流程
一条UPDATE语句的日志全过程:
1. 事务开始
2. 在Buffer Pool中找到目标数据页(若不在则从磁盘加载)
3. 修改Buffer Pool中的数据页(内存修改)
4. 生成Undo Log,记录修改前的旧值(用于回滚和MVCC)
5. 生成Redo Log,记录数据页的物理修改,状态为 prepare
6. 事务提交
7. 将Binlog写入磁盘(sync_binlog=1时)
8. 将Redo Log状态从 prepare 改为 commit,并刷盘
9. 事务完成,后台异步将脏页刷回磁盘
5.3 崩溃恢复时的决策逻辑
宕机重启后,InnoDB根据以下规则决策:
| Redo Log状态 | Binlog状态 | 恢复决策 |
|---|---|---|
| prepare | 完整(已写入) | 提交事务(Binlog已记录,需要保证主从不丢失) |
| prepare | 不完整(未写入) | 回滚事务(Binlog没记录,即使恢复也会导致主从不一致) |
| commit | 任意 | 提交事务(Redo Log已确认) |
这套机制保证了Redo Log和Binlog逻辑上的一致性,是MySQL数据安全的基石。
6、总结
| 日志类型 | 所属层级 | 日志类型 | 写入方式 | 核心职责 |
|---|---|---|---|---|
| Redo Log | InnoDB引擎层 | 物理日志(页级别修改) | 循环写,固定大小 | 持久性(D):崩溃恢复 |
| Undo Log | InnoDB引擎层 | 逻辑日志(反向操作) | 追加写,可回收 | 原子性(A)+ MVCC |
| Binlog | Server层 | 逻辑日志(SQL或行变更) | 追加写,按大小滚动 | 主从复制 + 时间点恢复 |
三者内在关联:
- Undo Log是MVCC的数据基础——版本链完全依赖Undo Log存储
- Redo Log是整套体系的“安全基石”——不仅保障数据页,也保障Undo Log的持久化(Undo Log的修改同样会生成Redo Log)
- 两阶段提交是Redo Log和Binlog保持一致的协同机制
附录:生产环境推荐配置
[mysqld]
# Redo Log——保障持久性
innodb_log_file_size = 2G # 单个redo文件大小,建议2-4G
innodb_log_files_in_group = 4 # 文件数量,总大小8-16G
innodb_flush_log_at_trx_commit = 1 # 每次提交刷盘(核心业务必选)
innodb_log_buffer_size = 64M # 日志缓冲区
# Undo Log——控制膨胀
innodb_max_undo_log_size = 2G # 最大undo空间,触发截断
innodb_undo_log_truncate = ON # 开启自动截断
innodb_purge_threads = 4 # 后台清理线程数
# Binlog——主从复制基础
server_id = 1 # 集群内唯一
log_bin = /data/mysql-bin # binlog路径和文件名前缀
binlog_format = ROW # 推荐行格式,数据最安全
sync_binlog = 1 # 每次提交刷盘
binlog_expire_logs_seconds = 604800 # 自动清理7天前的binlog
监控要点:关注Innodb_log_waits(Redo Log写入等待,应趋近于0)和information_schema.innodb_trx中的长事务。
更多推荐



所有评论(0)