news 2026/5/8 6:32:30

第六篇:Redo Log与Binlog——崩溃恢复的底层保障

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第六篇:Redo Log与Binlog——崩溃恢复的底层保障

前言

在前面的文章中,我们理解了事务的隔离级别和锁机制。但还有一个根本性问题没有解决:事务提交后,数据真的安全了吗?数据库突然宕机,重启后怎么恢复?

这就涉及到MySQL最核心的日志系统——Redo Log和Binlog

面试中,这个问题是区分"会用MySQL"和"理解MySQL"的分水岭:

“Redo Log和Binlog有什么区别?”
“为什么需要两阶段提交?”
“一条UPDATE语句在MySQL中到底经历了什么?”

如果你只能回答"Redo Log用于崩溃恢复,Binlog用于主从复制",面试官会继续追问到你说出两阶段提交的具体流程。本文帮你准备好这个问题的完整答案。

本文核心问题:

  1. WAL(Write-Ahead Logging)是什么?为什么写日志比直接写磁盘快?
  2. Redo Log和Binlog各自的作用和区别?
  3. 一条UPDATE语句在InnoDB中经历了什么?
  4. 两阶段提交(2PC)怎么保证两个日志的一致性?
  5. 崩溃恢复怎么做的?为什么能恢复已提交的事务?
  6. Redo Log的innodb_flush_log_at_trx_commit参数怎么配置?
  7. Binlog的三种格式(STATEMENT、ROW、MIXED)有什么区别?

读完本文,你将对MySQL的日志系统拥有从原理到配置的完整理解。


一、WAL机制——为什么先写日志?

疑问:为什么MySQL不直接把数据写磁盘,而是先写日志?写日志不是多了一步吗?

回答:因为顺序写远快于随机写。直接写数据是随机IO,可能需要寻道+旋转等待;写Redo Log是追加写,磁盘顺序写入的速度接近内存速度。

1.1 没有WAL时

UPDATE user SET age = 25 WHERE id = 1; 1. 从磁盘找到id=1所在的数据页(随机IO,约5-10ms) 2. 修改数据页中age的值 3. 将修改后的数据页写回磁盘(随机IO,约5-10ms)

问题:修改一行数据,需要两次随机磁盘IO。如果一次事务修改了100行分散在不同数据页中,就需要200次随机IO,性能完全不可接受。

1.2 有WAL时

1. 从磁盘找到id=1所在的数据页(随机读,约5-10ms) 2. 修改Buffer Pool中该数据页的值(内存操作) 3. 将修改记录追加写入Redo Log文件(顺序写,极快) 4. 返回客户端"提交成功" 5. 后台线程异步将脏页刷回磁盘(Checkpoint)

一个修改操作,最多一次随机读(如果数据页已在Buffer Pool中则这一步也可以省掉),加一次顺序写。写Redo Log是追加写,磁盘磁头不需要移动,效率比随机写高几个数量级。

1.3 WAL的核心思想

用顺序写日志替代随机写数据页,先保障持久性,后台再慢慢同步。


二、Redo Log——物理日志

疑问:Redo Log到底存了什么?为什么它是"物理日志"?

回答:Redo Log记录的是"某个数据页上,在某个偏移量处,修改了什么值"。它是物理层面的记录——哪个表空间、哪个页、哪个位置、改了什么。

2.1 Redo Log的结构

Redo Log文件:ib_logfile0, ib_logfile1 循环写入:写完第一个文件写第二个,写满后从头覆盖 Redo Log的记录格式(简化): ┌──────────┬──────────┬──────────┬──────────┐ │ 表空间ID │ 页号 │ 偏移量 │ 新数据 │ └──────────┴──────────┴──────────┴──────────┘ 例如:UPDATE user SET age=25 WHERE id=1 → 记录:表空间4,页号100,偏移量120,age改为25

2.2 Redo Log的刷盘策略

innodb_flush_log_at_trx_commit控制Redo Log何时刷盘:

策略安全性性能
0每秒刷盘一次低(可能丢失1秒内的已提交事务)最高
1每次提交时刷盘高(事务一旦提交就不会丢失)中等
2每次提交时写OS缓存,每秒刷盘中(OS宕机会丢,MySQL宕机不丢)较高

生产环境建议设为1。这个决定是在"数据安全性"和"写入性能"之间做权衡——每次提交都刷盘保证了Crash Safe,但磁盘IO压力增大。如果应用可以接受极端情况下丢失1秒的事务(如日志类数据),可以设为0或2换取更高的写入吞吐。

2.3 Redo Log的两阶段刷盘凭证

Redo Log记录了事务ID,且在Prepare阶段已经落盘。崩溃恢复时,事务ID在Redo Log中清晰可辨,这是两阶段提交和崩溃恢复的基础——后面会展开讲。


三、Binlog——逻辑日志

疑问:Binlog和Redo Log有什么不同?为什么MySQL需要两种日志?

回答:Binlog是MySQL Server层的逻辑日志,记录的是SQL语句或行变更的逻辑。Redo Log是InnoDB引擎层的物理日志,记录的是数据页的物理修改。两者职责不同。

3.1 Binlog的三种格式

格式记录内容优势劣势
STATEMENT记录SQL语句原文日志量小部分函数(NOW、UUID)在主从间结果不同
ROW记录每行数据的具体变更精确,不会出现不一致日志量大(批量UPDATE会产生大量行记录)
MIXED默认用STATEMENT,特殊情况自动切换ROW兼顾性能与一致性不够纯粹,排查时不确定到底用了哪种格式

生产建议用ROW。STATEMENT虽然日志量小,但不确定性函数(NOW、UUID等)导致的主从数据不一致问题更难恢复——数据不一致的修复成本远高于日志量的存储成本。

3.2 Binlog的刷盘策略

sync_binlog控制Binlog何时刷盘:

策略安全性
0交给操作系统决定何时刷盘
1每次提交时刷盘
N每N次提交刷盘一次

3.3 Redo Log vs Binlog 核心对比

维度Redo LogBinlog
产生层InnoDB引擎层MySQL Server层
记录内容物理日志(数据页修改)逻辑日志(SQL或行变更)
写入方式循环写(空间固定)追加写(文件不断增长)
用途崩溃恢复主从复制、数据恢复
大小固定大小(通常几百MB到几GB)不断增长,需定期清理
刷盘参数innodb_flush_log_at_trx_commitsync_binlog

四、一条UPDATE语句的完整旅程

疑问:执行UPDATE user SET age=25 WHERE id=1,MySQL内部到底发生了什么?

回答:一条简单的UPDATE语句,在MySQL内部经历了从Server层到引擎层的完整协作。这是面试中展示你理解深度的经典问题。

┌─────────────────────────────────────────────────────────────────┐ │ 1. Server层 - 连接器:接收客户端连接,获取用户权限 │ ├─────────────────────────────────────────────────────────────────┤ │ 2. Server层 - 分析器:解析SQL语法,生成语法树 │ ├─────────────────────────────────────────────────────────────────┤ │ 3. Server层 - 优化器:选择索引(id=1走主键),生成执行计划 │ ├─────────────────────────────────────────────────────────────────┤ │ 4. Server层 - 执行器:调用InnoDB引擎接口 │ │ ↓ │ │ 5. InnoDB引擎层: │ │ → 检查id=1的数据页是否在Buffer Pool中 │ │ → 不在 → 从磁盘读入Buffer Pool │ │ → 将修改前的旧值写入Undo Log(用于回滚和MVCC) │ │ → 修改Buffer Pool中该数据页的age=25 │ │ → 将修改记录写入Redo Log Buffer(Prepare状态) │ │ → 返回执行器:修改完成 │ │ ↓ │ │ 6. Server层 - 执行器: │ │ → 将修改记录写入Binlog Cache │ │ → 提交事务时,先将Redo Log置为Prepare状态并刷盘 │ │ → 再将Binlog刷盘 │ │ → 最后将Redo Log置为Commit状态(两阶段提交完成) │ │ ↓ │ │ 7. 返回客户端:"Query OK, 1 row affected" │ └─────────────────────────────────────────────────────────────────┘

关键认知:UPDATE操作在Buffer Pool里改完数据页并记录Redo Log后不需要立即刷脏页回磁盘。Redo Log已经记录了这次更改,即使此时宕机也可以在崩溃恢复时重放Redo Log复原数据。脏页由后台线程在合适的时机批量刷入磁盘,这个机制叫做Checkpoint。


五、两阶段提交——保证双日志一致性

疑问:为什么需要两阶段提交?没有两阶段提交会怎样?

回答:两阶段提交确保Redo Log和Binlog要么都写入成功,要么都失败——在任何一个环节宕机,恢复后两个日志保持一致,从而保证主从数据的一致。

5.1 没有两阶段提交的问题

场景一:先写Redo Log,后写Binlog

事务A提交:Redo Log写成功 → 此时宕机 → Binlog未写入 恢复后: 主库通过Redo Log恢复了事务A的修改 ✓ 从库通过Binlog同步,没有事务A的记录 ✗ → 主从数据不一致!

场景二:先写Binlog,后写Redo Log

事务A提交:Binlog写成功 → 此时宕机 → Redo Log未写入 恢复后: 主库的Redo Log中没有事务A → 主库没有恢复事务A的修改 ✗ 从库通过Binlog同步,有事务A的记录 ✓ → 主从数据不一致!

5.2 两阶段提交流程

阶段一:Prepare 1. InnoDB将Redo Log标记为Prepare状态 2. 将Redo Log刷盘 阶段二:Commit 3. Server层将Binlog刷盘 4. InnoDB将Redo Log标记为Commit状态(异步,不需要等刷盘) 5. 事务提交完成

5.3 崩溃恢复时的判断逻辑

重启时扫描Redo Log,找到所有处于Prepare状态的事务: Prepare状态的事务 → 去Binlog中查找是否有对应的记录 如果Binlog中也有 → 说明两阶段完成 → 提交这个事务 如果Binlog中没有 → 说明第二阶段完成前就宕机了 → 回滚这个事务

Binlog是判断的依据:Binlog成功写完,说明整个事务流程已经走过最关键的节点,恢复时可以提交;Binlog没写完,说明第二阶段中断,恢复时应该回滚。


六、崩溃恢复的完整流程

疑问:数据库宕机后,重启时是怎么恢复的?

回答:崩溃恢复的本质是"重放Redo Log + 回滚未提交事务"。核心目标是恢复到宕机前最后一个已提交事务的状态。

1. 扫描Redo Log,找到所有Prepare状态的事务 2. 去Binlog中查找这些事务的完整记录 3. 如果Binlog中有 → 提交这个事务(重做) 4. 如果Binlog中没有 → 回滚这个事务 5. 对未提交事务,通过Undo Log回滚

已提交但脏页未刷盘的事务:Redo Log中有完整记录 → 重放Redo Log → 数据恢复。这类事务在宕机前"返回了commit成功给客户端,但后台还没来得及把脏页写入磁盘"。Redo Log的存在保证了这些事务的数据不会丢失。

未提交的事务:Redo Log中没有Commit标记 → Binlog中也没有记录 → 通过Undo Log回滚。这类事务在宕机前从未commit过,崩溃恢复后它们应该被当作"从未发生过"。


七、Redo Log与Binlog协同总结

Redo Log确保Crash Safe: 事务提交成功 → Redo Log中有Commit标记 → 即使宕机也能恢复 Binlog确保主从一致: Binlog中有了记录 → 从库能复制 → 主从数据一致 两阶段提交确保双日志一致: Prepare(Redo) → Commit(Binlog) → Commit(Redo) 崩溃恢复时,以Binlog为准判断事务是否真正提交

总结

  • WAL让MySQL的写入速度飞升——用顺序写日志替代随机写数据页,把一次修改的IO成本从两次随机IO降到一次随机读+一次顺序写
  • Redo Log记录页的物理修改(哪个表空间、哪个页、哪个偏移量),用于InnoDB崩溃恢复
  • Binlog记录SQL或行的逻辑变更,用于Server层主从复制和数据恢复
  • 一条UPDATE经历了完整的Server层→引擎层→日志刷盘流程:Buffer Pool修改→Undo Log记录旧版本→Redo Log Prepare→Binlog刷盘→Redo Log Commit
  • 两阶段提交保证双日志一致:Prepare和Commit之间如果宕机,恢复时以Binlog为准——Binlog中有则提交,没有则回滚
  • 崩溃恢复以Binlog为准:Prepare状态的事务看Binlog有没有对应记录来决定提交还是回滚
  • 生产配置建议innodb_flush_log_at_trx_commit=1+sync_binlog=1最高安全性,2+00+0是性能优先不同安全等级的选择

下一篇预告:MySQL索引原理(七)——慢查询分析与SQL优化实战。整合前六篇的索引和事务知识,用Explain和慢查询日志做真实的SQL优化案例分析。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 6:27:30

dotnet-skills:让AI助手掌握现代.NET开发最佳实践

1. 项目概述如果你是一位 .NET 开发者,并且正在使用像 Claude、GitHub Copilot、Gemini 或 Codex 这样的 AI 编程助手,那么你很可能经历过这样的挫败感:你满怀期待地输入“使用 Entity Framework Core 创建一个包含用户和订单的数据库上下文”…

作者头像 李华
网站建设 2026/5/8 6:26:37

为什么很多人 DFS 写得飞起,一到「矩阵最长递增路径」就彻底懵了?

为什么很多人 DFS 写得飞起,一到「矩阵最长递增路径」就彻底懵了? 有一类算法题,非常容易让人产生错觉。 看起来只是: 矩阵 + DFS结果一写。 不是超时。 就是死循环。 再不然: 明明逻辑没错 结果性能直接爆炸而「矩阵中的最长递增路径(Longest Increasing Path in a…

作者头像 李华
网站建设 2026/5/8 6:25:35

Vestige:一个被遗忘的Node.js极简API框架遗珠

1. 项目概述:一个被遗忘的Web框架遗珠在Web开发这个快速迭代的领域里,我们每天都能听到关于React、Vue、Next.js这些明星框架的讨论。但如果你像我一样,在这个行业里摸爬滚打了十几年,就会知道,真正解决问题的往往不是…

作者头像 李华
网站建设 2026/5/8 6:19:59

前端安全:XSS防御最佳实践

前端安全:XSS防御最佳实践 前言 XSS(Cross-Site Scripting,跨站脚本攻击)是一种常见的前端安全漏洞,它允许攻击者在用户的浏览器中执行恶意脚本。XSS攻击可以导致会话劫持、数据泄露、网站篡改等严重问题。今天&#x…

作者头像 李华
网站建设 2026/5/8 6:19:56

不止于性能:拆解STM32H7多域架构如何重塑你的嵌入式应用设计思路

不止于性能:拆解STM32H7多域架构如何重塑你的嵌入式应用设计思路 在嵌入式系统设计领域,性能参数表上的数字竞赛已经持续了太久。当我们把目光从MHz和DMIPS的简单对比中移开,STMicroelectronics的STM32H7系列带来的真正革新才浮出水面——它不…

作者头像 李华
网站建设 2026/5/8 6:11:37

高德顺风车xck、an参数逆向

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包 内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!侵权通过头像私信或名字简介叫我删除博…

作者头像 李华