无论在工作还是在面试中MySQL 的Redo Log、Undo Log、Binlog的三大日志体系都是一个绕不开的硬核技术点。本文将从零开始详细介绍每一类日志的作用、工作原理、写入机制以及三者如何配合支撑事务的 ACID 特性。一、为什么需要了解三大日志数据库操作面临一个经典矛盾既要保证数据不丢失持久性又要追求高性能。现代数据库给出的答案是WALWrite-Ahead Logging预写日志技术——先写日志后写磁盘。而 MySQL 的日志体系比这更复杂它把功能拆解到了三种不同的日志中。MySQL 的日志有很多种错误日志、慢查询日志等其中最重要、最核心的三种分别是Binlog归档日志Server 层Redo Log重做日志InnoDB 引擎层Undo Log回滚日志InnoDB 引擎层用一句话概括三者分工Redo Log 保“持久”——宕机不丢数据Undo Log 保“原子”——做错了能撤回Binlog 保“可追溯、可同步”——主从复制、数据恢复二、Redo Log崩溃恢复的守护者2.1 为什么需要 Redo LogMySQL 的 InnoDB 引擎使用 Buffer Pool 作为数据页的内存缓存所有数据的修改会先在 Buffer Pool 中完成再将“脏页”异步刷回磁盘。这样设计的最大好处是避免频繁的磁盘随机 I/O直接写磁盘太慢但也带来了一个致命隐患如果在事务提交之后、脏页落盘之前发生宕机内存中的数据就会全部丢失。Redo Log 就是专门解决这个问题的。2.2 Redo Log 的核心特性物理日志Redo Log 记录的是“在某个数据页的某个偏移量上做了什么修改”而不是 SQL 语句本身。WALWrite-Ahead Logging修改数据之前先把 Redo Log 写入磁盘数据页可以稍后异步写入。先写日志再写数据这是 InnoDB 实现 crash-safe 的核心机制。循环写 固定大小Redo Log 不是无限增长的而是用一组固定大小的文件循环写入。MySQL 8.0.30 之前默认两个文件ib_logfile0 和 ib_logfile1之后支持动态配置容量。LSNLog Sequence Number日志序列号每条 Redo Log 都有一个全局唯一的 LSN用于标识日志记录顺序也用于判断哪些数据已经落盘、哪些还需要恢复。2.3 Redo Log 的结构与写入流程Redo Log 在磁盘上的组织结构由两个指针控制write pos当前日志写入位置边写边后移checkpoint当前已落盘数据对应的日志位置之后的日志还需要用于恢复当 write pos 追上 checkpoint 时表示日志文件写满了InnoDB 会暂停写入先将一些脏页强制刷盘把 checkpoint 向前推进腾出空间后继续写入。完整的写入流程分为三步事务执行时修改操作先写入内存中的Redo Log Buffer。事务提交时根据innodb_flush_log_at_trx_commit参数决定刷盘策略1默认每次提交都将 Buffer 刷到 OS cache 并调用fsync()落盘最安全性能稍低0每秒刷盘一次事务提交时不触发刷盘性能最好但可能丢失最近 1 秒的事务2每次提交刷到 OS cache只保证写入操作系统缓存不保证物理落盘每秒fsync()一次后台异步后台线程在系统空闲时将 Buffer Pool 中的脏页异步刷入磁盘。2.4 MySQL 8.0 的 Redo Log 优化MySQL 8.0 对 Redo Log 做了重要改进无锁化设计用户线程可以并行写入 Log Buffer不再串行等待大幅提升高并发下的写入性能独立后台线程Log Writer 负责将 Buffer 写入 OS cacheLog Flusher 负责调用fsync()真正落盘分工明确动态容量调整MySQL 8.0.30 引入innodb_redo_log_capacity参数可以在线调整 Redo Log 总容量不再依赖ib_logfile*文件的固定大小和数量三、Undo Log事务回滚与 MVCC 的基石3.1 Undo Log 的核心作用Undo Log 的主要职责有两个事务回滚当事务执行失败或主动回滚时将数据恢复到修改前的状态保证事务的原子性MVCC多版本并发控制为其他事务提供一致性读视图让读操作不加锁读写互不阻塞极大提升并发性能3.2 逻辑日志与存储结构Undo Log 是一种逻辑日志记录的是反向操作执行INSERT→ 记录对应的主键信息回滚时DELETE执行DELETE→ 记录完整的行数据回滚时INSERT回去执行UPDATE→ 记录修改前的旧值回滚时反向UPDATEUndo Log 存储在 InnoDB 的回滚段rollback segment中每个回滚段包含 1024 个 undo slot。3.3 隐藏字段与版本链InnoDB 的行记录中隐藏着三个重要字段DB_TRX_ID6 字节最后一次修改该行的事务 ID全局递增DB_ROLL_PTR7 字节回滚指针指向 Undo Log 中的上一个版本DB_ROW_ID6 字节行 ID仅在表没有主键时使用当事务 A 修改某行数据时步骤如下将修改前的完整数据写入 Undo Log更新当前行的DB_TRX_ID 事务A的ID更新当前行的DB_ROLL_PTR指向刚写入的 Undo Log 记录修改数据本身第二次被事务 B 修改时同样的流程会继续追加新的 Undo Log。事务 B 的DB_ROLL_PTR指向事务 A 的版本以此类推形成一条单向版本链最靠近当前行的是最新版本链表尾部是最古老的版本。3.4 MVCC 与 Read ViewMVCC 的全称是多版本并发控制Multi-Version Concurrency Control。当一个事务需要读取某行数据时它不会直接读取当前行而是沿着版本链往回找找到第一个“可见”的版本。Read View决定了可见性规则它包含四个核心部分m_ids生成 Read View 时系统中所有活跃的未提交事务 ID 列表min_trx_idm_ids中的最小值最早的未提交事务max_trx_id系统下一个要分配的事务 IDcreator_trx_id生成这个 Read View 的事务自己的 ID可见性判断逻辑对于版本链中的某个版本trx_id为 X若X creator_trx_id→ 当前事务自己修改的 →可见若X min_trx_id→ 该版本在 Read View 创建前就已提交 →可见若X max_trx_id→ 该版本在 Read View 创建之后才生成 →不可见若min_trx_id X max_trx_id在m_ids列表中未提交→不可见继续沿版本链往前找不在列表中已提交→可见这也就是为什么Read CommittedRC和Repeatable ReadRR两个隔离级别表现不同的根本原因RC 级别每次查询都会生成一个新的 Read View所以能立刻看到其他事务已提交的修改RR 级别只在事务启动时的第一次查询生成一个 Read View并一直复用从而保证了可重复读3.5 Undo Log 的生命周期与清理事务提交后Undo Log 不会立即删除因为可能还有其他事务依赖这些历史版本进行 MVCC 读取。InnoDB 使用后台的purge 线程定期扫描当一个 Undo Log 版本不再被任何活动的 Read View 所需要时才会被真正清理。Undo Log 分为两种类型Insert Undo Log插入操作产生事务提交后可直接删除其他事务不需要看到未插入的状态Update Undo Log更新和删除操作产生需要保留到没有事务依赖这些版本时四、Binlog主从复制与数据恢复的利器4.1 Binlog 是什么BinlogBinary Log二进制日志是MySQL Server 层的日志所有存储引擎InnoDB、MyISAM 等的更新操作都会被记录下来。它的核心用途有三个主从复制主库发送 binlog 给从库从库重放实现数据同步基于时间点的数据恢复利用全量备份 binlog 恢复到任意时间点数据审计追溯所有数据变更记录4.2 Binlog 的三种记录格式Binlog 的记录内容由binlog_format参数控制共有三种格式。格式记录内容优点缺点适用场景STATEMENT原始 SQL 语句日志量极小一条 SQL 更新百万行只占几十字节极易主从不一致NOW()、UUID()、RAND()等函数在从库重放结果不同极少用于生产ROW每行数据的前后镜像绝对一致支持闪回并行复制友好日志量巨大更新 10 万行产生 10 万条记录8.0 默认生产推荐MIXED自动混合确定性 SQL 用 STATEMENT其他用 ROW平衡性能和一致性存在隐藏风险不推荐主动使用历史项目过渡MySQL 5.7.7 之后ROW 已是默认模式。ROW 格式还有一个重要优势它支持基于 Binlog 的闪回flashback。通过解析 ROW 格式的 Binlog可以把DELETE转成INSERT、把UPDATE转成反向UPDATE对误操作进行精确恢复。4.3 Binlog 的写入机制与文件轮转Binlog 采用追加写模式不会覆盖已有数据。一个事务的 Binlog 会先写入线程私有的binlog cache事务提交时一次性写入 binlog 文件。刷盘时机由sync_binlog参数控制1默认每次事务提交都执行fsync()落盘最安全性能折中0不主动fsync()由操作系统决定何时落盘性能最好但风险最高N每 N 个事务执行一次fsync()N1折中方案文件轮转规则使用mysql-bin.000001、mysql-bin.000002…… 递增命名通过max_binlog_size控制单文件大小默认 1GB执行FLUSH LOGS可手动切换文件通过expire_logs_days或PURGE BINARY LOGS自动清理旧文件4.4 Binlog 数据恢复实战如果开启了 Binlog且格式为 ROW误操作一般都能恢复。典型的恢复流程是步骤一确认 Binlog 已开启sqlSHOW VARIABLES LIKE log_bin%;步骤二找到误操作的位置bash# 解析 binlog 找到误删的 position 范围 mysqlbinlog --no-defaults -vv mysql-bin.000011 binlog_content.txt # 在输出中搜索 DELETE/UPDATE 的关键词定位 start-pos 和 stop-pos步骤三用 mysqlbinlog 生成恢复 SQLbash# 提取误操作前后的日志区间 mysqlbinlog --start-position1969 --stop-position902120 mysql-bin.000011 | mysql -uroot -p如果是误删且无备份的紧急场景可以基于 ROW 格式生成反向 SQLbashmysqlbinlog --base64-outputdecode-rows -v mysql-bin.000011 \ --start-datetime2025-12-27 09:00:00 \ --stop-datetime2025-12-27 09:30:00 | \ sed -n /### DELETE /{s/### DELETE/### INSERT/;p} rollback.sql恢复的基本原则先全量备份再应用增量 binlog恢复前必须在测试环境验证大事务闪回可能影响性能高并发场景需谨慎。五、三大日志协同与两阶段提交2PC5.1 为什么需要两阶段提交单个事务在 MySQL 中最终会涉及两个独立的日志系统InnoDB 引擎层的 Redo Log 和 Server 层的 Binlog。如果先写 Redo Log 后写 Binlog或反过来都可能出现日志不一致的“半成功状态”。先写 Redo Log后写 BinlogRedo Log 已持久化、Binlog 未写入时宕机 → 主库通过 Redo Log 恢复数据但 Binlog 没有这个事务从库丢失更新主从不一致先写 Binlog后写 Redo LogBinlog 已写入、Redo Log 未提交时宕机 → 主库回滚该事务但 Binlog 记录了这个变更从库多了这个事务同样主从不一致为了解决这个问题MySQL 引入了两阶段提交2PC协议。5.2 两阶段提交的完整流程两阶段提交将事务提交拆分为Prepare 阶段和Commit 阶段Prepare 阶段事务执行过程中修改操作不断写入 Undo Log 和 Redo Log Buffer事务提交时InnoDB 将 Redo Log Buffer 刷盘将事务状态标记为PREPARE同时将内部 XIDXA 事务 ID写入 Redo LogCommit 阶段将 XID 写入 Binlog将 Binlog 刷盘受sync_binlog参数控制调用 InnoDB 的提交接口将 Redo Log 的事务状态从PREPARE改为COMMIT向客户端返回“提交成功”这里有一个精妙的设计Commit 阶段的最后一步Redo Log 改为COMMIT只需要写入操作系统缓存OS page cache不需要立即fsync()。因为只要 Binlog 成功落盘即使此时崩溃重启Redo Log 中的PREPARE状态也会被认可——Binlog 是最终判断依据。5.3 崩溃恢复的判断逻辑MySQL 崩溃重启后扫描 Redo Log 中所有处于PREPARE状态的事务。对于每个这样的未决事务根据 Redo Log 中记录的 XID去 Binlog 中查找对应的事务记录如果 Binlog 中存在完整记录→ 该事务在提交阶段实际上已经成功 →提交事务如果 Binlog 中不存在记录→ 该事务在 Commit 阶段之前就已崩溃 →回滚事务核心原则以 Binlog 的完整性作为事务是否提交的最终判断标准。这个机制保证了无论何时崩溃主库的数据一定等于“从 Binlog 中恢复从库时得到的数据”主从数据最终一致。5.4 一个完整的 update 语句流程以执行UPDATE t SET c c 1 WHERE id 2为例完整流程如下事务开始记录 Undo Log保存修改前的旧值执行更新从磁盘/缓存读取数据页到 Buffer Pool在 Buffer Pool 中修改数据将修改动作写入 Redo Log Buffer内存Prepare 阶段Redo Log Buffer 刷盘事务状态置为PREPARECommit 阶段写入 Binlog 并刷盘Redo Log 状态改为COMMIT后台线程择机将 Buffer Pool 中的脏页刷回磁盘异步这个设计的精妙之处在于事务提交前只写了顺序的 Redo Log 和 Binlog写日志很快真正的随机写磁盘操作被延迟到了异步阶段保证了高性能而两阶段提交又保证了分布式一致性实现了性能与可靠性的平衡。六、三大日志核心对比速查表对比维度BinlogRedo LogUndo Log所属层级Server 层InnoDB 引擎层InnoDB 引擎层日志类型逻辑日志SQL/行变化物理日志页修改逻辑日志反向操作写入方式追加写生成新文件循环写大小固定追加写定期清理核心作用主从复制、数据恢复崩溃恢复crash-safe事务回滚、MVCC能否保证持久性❌ 不能✅ 能❌ 不能能否保证原子性❌ 不能❌ 不能✅ 能刷盘参数sync_binloginnodb_flush_log_at_trx_commit无独立参数生命周期手动/自动清理循环覆盖purge 线程清理七、总结Redo Log、Undo Log 与 Binlog 三者各司其职共同构成了 MySQL 事务和高可用能力的基石Undo Log原子性基石记录修改前的旧数据支持事务回滚也是 MVCC 多版本并发控制的关键Redo Log持久性基石基于 WAL 机制保证崩溃恢复能力通过两阶段提交与 Binlog 协同确保主从数据一致Binlog可复制性基石记录所有变更操作支撑主从复制和时间点数据恢复每一类日志单独拿出来都不复杂但三者协同的机制特别是 MVCC 版本链 Read View、两阶段提交 崩溃恢复才是 MySQL 真正硬核的地方。掌握了这个体系无论是面试还是线上问题排查都能更加从容。下一步学习建议实际验证innodb_flush_log_at_trx_commit和sync_binlog不同取值对性能的影响搭建一主一从环境观察 binlog 的传输与重放过程模拟一次误删操作用 mysqlbinlog 完整走一遍数据恢复流程⚠️ 务必在测试环境进行