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中的长事务。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐