当前位置: 首页 > news >正文

MySQL 三大日志:Redo Log、Undo Log 和 Binlog 完全解析

无论在工作还是在面试中,MySQL 的Redo Log、Undo Log、Binlog的三大日志体系都是一个绕不开的硬核技术点。本文将从零开始,详细介绍每一类日志的作用、工作原理、写入机制,以及三者如何配合支撑事务的 ACID 特性。

一、为什么需要了解三大日志?

数据库操作面临一个经典矛盾:既要保证数据不丢失(持久性),又要追求高性能。现代数据库给出的答案是WAL(Write-Ahead Logging,预写日志)技术——先写日志,后写磁盘。而 MySQL 的日志体系比这更复杂,它把功能拆解到了三种不同的日志中。

MySQL 的日志有很多种(错误日志、慢查询日志等),其中最重要、最核心的三种分别是:

  • Binlog(归档日志,Server 层)

  • Redo Log(重做日志,InnoDB 引擎层)

  • Undo Log(回滚日志,InnoDB 引擎层)

用一句话概括三者分工:

  • Redo Log 保“持久”——宕机不丢数据

  • Undo Log 保“原子”——做错了能撤回

  • Binlog 保“可追溯、可同步”——主从复制、数据恢复

二、Redo Log:崩溃恢复的守护者

2.1 为什么需要 Redo Log?

MySQL 的 InnoDB 引擎使用 Buffer Pool 作为数据页的内存缓存,所有数据的修改会先在 Buffer Pool 中完成,再将“脏页”异步刷回磁盘。

这样设计的最大好处是避免频繁的磁盘随机 I/O(直接写磁盘太慢),但也带来了一个致命隐患:如果在事务提交之后、脏页落盘之前发生宕机,内存中的数据就会全部丢失。

Redo Log 就是专门解决这个问题的。

2.2 Redo Log 的核心特性

物理日志:Redo Log 记录的是“在某个数据页的某个偏移量上做了什么修改”,而不是 SQL 语句本身。

WAL(Write-Ahead Logging):修改数据之前,先把 Redo Log 写入磁盘,数据页可以稍后异步写入。先写日志再写数据,这是 InnoDB 实现 crash-safe 的核心机制。

循环写 + 固定大小:Redo Log 不是无限增长的,而是用一组固定大小的文件循环写入。MySQL 8.0.30 之前默认两个文件(ib_logfile0 和 ib_logfile1),之后支持动态配置容量。

LSN(Log Sequence Number,日志序列号):每条 Redo Log 都有一个全局唯一的 LSN,用于标识日志记录顺序,也用于判断哪些数据已经落盘、哪些还需要恢复。

2.3 Redo Log 的结构与写入流程

Redo Log 在磁盘上的组织结构由两个指针控制:

  • write pos:当前日志写入位置,边写边后移

  • checkpoint:当前已落盘数据对应的日志位置,之后的日志还需要用于恢复

当 write pos 追上 checkpoint 时,表示日志文件写满了,InnoDB 会暂停写入,先将一些脏页强制刷盘,把 checkpoint 向前推进,腾出空间后继续写入。

完整的写入流程分为三步:

  1. 事务执行时:修改操作先写入内存中的Redo Log Buffer

  2. 事务提交时:根据innodb_flush_log_at_trx_commit参数决定刷盘策略:

    • 1(默认):每次提交都将 Buffer 刷到 OS cache 并调用fsync()落盘,最安全,性能稍低

    • 0:每秒刷盘一次,事务提交时不触发刷盘,性能最好,但可能丢失最近 1 秒的事务

    • 2:每次提交刷到 OS cache(只保证写入操作系统缓存,不保证物理落盘),每秒fsync()一次

  3. 后台异步:后台线程在系统空闲时,将 Buffer Pool 中的脏页异步刷入磁盘。

2.4 MySQL 8.0 的 Redo Log 优化

MySQL 8.0 对 Redo Log 做了重要改进:

  • 无锁化设计:用户线程可以并行写入 Log Buffer,不再串行等待,大幅提升高并发下的写入性能

  • 独立后台线程:Log Writer 负责将 Buffer 写入 OS cache,Log 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→ 记录修改前的旧值(回滚时反向UPDATE

Undo Log 存储在 InnoDB 的回滚段(rollback segment)中,每个回滚段包含 1024 个 undo slot。

3.3 隐藏字段与版本链

InnoDB 的行记录中隐藏着三个重要字段:

  • DB_TRX_ID(6 字节):最后一次修改该行的事务 ID,全局递增

  • DB_ROLL_PTR(7 字节):回滚指针,指向 Undo Log 中的上一个版本

  • DB_ROW_ID(6 字节):行 ID,仅在表没有主键时使用

当事务 A 修改某行数据时,步骤如下:

  1. 将修改前的完整数据写入 Undo Log

  2. 更新当前行的DB_TRX_ID = 事务A的ID

  3. 更新当前行的DB_ROLL_PTR指向刚写入的 Undo Log 记录

  4. 修改数据本身

第二次被事务 B 修改时,同样的流程会继续追加新的 Undo Log。事务 B 的DB_ROLL_PTR指向事务 A 的版本,以此类推,形成一条单向版本链,最靠近当前行的是最新版本,链表尾部是最古老的版本。

3.4 MVCC 与 Read View

MVCC 的全称是多版本并发控制(Multi-Version Concurrency Control)。当一个事务需要读取某行数据时,它不会直接读取当前行,而是沿着版本链往回找,找到第一个“可见”的版本。

Read View决定了可见性规则,它包含四个核心部分:

  • m_ids:生成 Read View 时,系统中所有活跃的未提交事务 ID 列表

  • min_trx_idm_ids中的最小值(最早的未提交事务)

  • max_trx_id:系统下一个要分配的事务 ID

  • creator_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 Committed(RC)Repeatable Read(RR)两个隔离级别表现不同的根本原因:

  • 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 是什么?

Binlog(Binary 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()(N>1),折中方案

文件轮转规则:

  • 使用mysql-bin.000001mysql-bin.000002…… 递增命名

  • 通过max_binlog_size控制单文件大小(默认 1GB)

  • 执行FLUSH LOGS可手动切换文件

  • 通过expire_logs_daysPURGE BINARY LOGS自动清理旧文件

4.4 Binlog 数据恢复实战

如果开启了 Binlog(且格式为 ROW),误操作一般都能恢复。典型的恢复流程是:

步骤一:确认 Binlog 已开启

sql

SHOW VARIABLES LIKE 'log_bin%';

步骤二:找到误操作的位置

bash

# 解析 binlog 找到误删的 position 范围 mysqlbinlog --no-defaults -vv mysql-bin.000011 > binlog_content.txt # 在输出中搜索 DELETE/UPDATE 的关键词定位 start-pos 和 stop-pos

步骤三:用 mysqlbinlog 生成恢复 SQL

bash

# 提取误操作前后的日志区间 mysqlbinlog --start-position=1969 --stop-position=902120 mysql-bin.000011 | mysql -uroot -p

如果是误删且无备份的紧急场景,可以基于 ROW 格式生成反向 SQL:

bash

mysqlbinlog --base64-output=decode-rows -v mysql-bin.000011 \ --start-datetime="2025-12-27 09:00:00" \ --stop-datetime="2025-12-27 09:30:00" | \ sed -n '/### DELETE /{s/### DELETE/### INSERT/;p}' > rollback.sql

恢复的基本原则:先全量备份,再应用增量 binlog;恢复前必须在测试环境验证;大事务闪回可能影响性能,高并发场景需谨慎。

五、三大日志协同与两阶段提交(2PC)

5.1 为什么需要两阶段提交?

单个事务在 MySQL 中最终会涉及两个独立的日志系统:InnoDB 引擎层的 Redo Log 和 Server 层的 Binlog。如果先写 Redo Log 后写 Binlog,或反过来,都可能出现日志不一致的“半成功状态”。

  • 先写 Redo Log,后写 Binlog:Redo Log 已持久化、Binlog 未写入时宕机 → 主库通过 Redo Log 恢复数据,但 Binlog 没有这个事务,从库丢失更新,主从不一致

  • 先写 Binlog,后写 Redo Log:Binlog 已写入、Redo Log 未提交时宕机 → 主库回滚该事务,但 Binlog 记录了这个变更,从库多了这个事务,同样主从不一致

为了解决这个问题,MySQL 引入了两阶段提交(2PC)协议。

5.2 两阶段提交的完整流程

两阶段提交将事务提交拆分为Prepare 阶段Commit 阶段

Prepare 阶段

  • 事务执行过程中,修改操作不断写入 Undo Log 和 Redo Log Buffer

  • 事务提交时,InnoDB 将 Redo Log Buffer 刷盘

  • 将事务状态标记为PREPARE,同时将内部 XID(XA 事务 ID)写入 Redo Log

Commit 阶段

  • 将 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状态的事务。对于每个这样的未决事务:

  1. 根据 Redo Log 中记录的 XID,去 Binlog 中查找对应的事务记录

  2. 如果 Binlog 中存在完整记录→ 该事务在提交阶段实际上已经成功 →提交事务

  3. 如果 Binlog 中不存在记录→ 该事务在 Commit 阶段之前就已崩溃 →回滚事务

核心原则:以 Binlog 的完整性作为事务是否提交的最终判断标准。这个机制保证了无论何时崩溃,主库的数据一定等于“从 Binlog 中恢复从库时得到的数据”,主从数据最终一致。

5.4 一个完整的 update 语句流程

以执行UPDATE t SET c = c + 1 WHERE id = 2为例,完整流程如下:

  1. 事务开始,记录 Undo Log(保存修改前的旧值)

  2. 执行更新

    • 从磁盘/缓存读取数据页到 Buffer Pool

    • 在 Buffer Pool 中修改数据

    • 将修改动作写入 Redo Log Buffer(内存)

  3. Prepare 阶段

    • Redo Log Buffer 刷盘,事务状态置为PREPARE

  4. Commit 阶段

    • 写入 Binlog 并刷盘

    • Redo Log 状态改为COMMIT

  5. 后台线程择机将 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_commitsync_binlog不同取值对性能的影响

  • 搭建一主一从环境,观察 binlog 的传输与重放过程

  • 模拟一次误删操作,用 mysqlbinlog 完整走一遍数据恢复流程(⚠️ 务必在测试环境进行)

http://www.jsqmd.com/news/930178/

相关文章:

  • Avidemux2终极指南:5分钟掌握开源视频编辑神器
  • BetterNCM安装器:3步让网易云音乐变身全能播放器
  • YOLO26骨折识别检测系统:基于YOLOv26的高精度骨折影像智能诊断与分析平台(项目源码+数据集+模型权重+UI界面+python+深度学习+远程环境部署)
  • 手机证件照怎么生成?2026手机制作证件照的方法+软件推荐,保姆级教程手把手教你 - 软件小管家
  • 无线网卡监听模式全解析:从Managed到Monitor,你的网卡到底能干嘛?
  • UE5 CesiumForUnreal避坑指南:从加载本地倾斜模型到解决Sequence卡顿的实战记录
  • Arduino SPI驱动ILI9488触摸屏与轻量级GUI库设计实践
  • 高速电路地线并非越粗越好,背后原理你了解吗?
  • Sora 2驱动虚拟偶像视频量产:从模型微调、动捕对齐到实时渲染的7个工业级技术栈实操手册
  • Bilibili视频下载技术方案:构建个人数字媒体库的Python自动化工具
  • 极限竞速修改神器:Forza Mods AIO终极免费指南,打造你的专属游戏体验
  • Arduino驱动伺服电机:从PWM原理到电位器实时控制实践
  • UnityExplorer终极指南:如何轻松调试和修改Unity游戏?
  • TikTok 2026 NG OA 全真题复盘|四道题难度递进,Teleport Labyrinth 翻车率最高
  • STM32F103用ADC采样+LCD实时画波形,开箱即用工程包
  • 东莞家庭除臭虫全攻略:轻松告别烦人小虫,安心居住每刻 - 品牌优选官
  • 冒险岛游戏编辑终极指南:一站式.wz文件与地图编辑解决方案
  • 基于Micro:bit的声控手机定位器:双击拍手检测算法与嵌入式实践
  • 3分钟掌握ComfyUI IPAdapter Plus:让AI绘画学会“看图说话“的神器
  • 【限时解禁】Sora 2内部法线生成管线首次公开:含3类不可见约束条件、4层微分渲染校准机制与1套评估基准
  • OmenSuperHub:释放惠普暗影精灵游戏本全部潜力的开源控制中心
  • ITSM现代化转型:从成本中心到战略引擎的核心架构与实践
  • Linux内核里那个默默无闻的‘搬运工’:SWIOTLB的bounce buffer机制详解
  • 哪个做表AI工具好用?数以轻舟Agent用“说人话“重新定义Excel效率
  • 基于YOLO26深度学习的晶圆体缺陷识别检测系统(项目源码+数据集+模型权重+UI界面+python+深度学习+远程环境部署)
  • OpenUtau完全指南:免费开源虚拟歌手软件,让音乐创作触手可及
  • 基于MQTT与Node-RED的工业PLC与智能家居系统集成实践
  • 从ISA-95 Part 3出发:手把手拆解一个“标准版”MOM系统该有哪些功能模块
  • 4个核心模块深度解析:Pearcleaner如何实现macOS应用的彻底清理
  • 家具设计师必看的Sora 2视频工作流(从SketchUp模型到4K动态展示片仅需22分钟)