文章目录

在 MySQL 的面试和学习中,redo log(重做日志)和 binlog(归档日志)是绕不开的绝对核心。直观的记忆是,他们“一个是物理日志,一个是逻辑日志”,但在一些深入的拷打中,还有更多的细节和联系
本文对比 redolog、binlog,从日志架构中最让人困惑的三个痛点,梳理底层的推导逻辑。
痛点一:为什么写的方式一个是“循环”,一个是“追加”?
虽然我们已经知道 redo log 是循环写,binlog 是追加写。但为什么要这么设计?这取决于它们各自的历史使命。
1. Redo Log:防崩溃(循环写)
- 使命:保证事务的持久性。它记录的是“内存里的数据页改了什么,但还没来得及刷入磁盘的数据”。
- 为什么循环写?
一旦 MySQL 的后台线程把内存里的脏页真正刷入了磁盘(落盘),这部分 redo log 就彻底失去价值了(因为数据已经安全躺在磁盘里了)。
既然没用了,就不需要无限期保留。所以 InnoDB 把它设计成固定大小(比如 4 个 1GB 的文件),像一个环形空间,前面的数据落盘了,后面的新日志就可以直接覆盖上来。这样极大地节省了磁盘空间。
2. Binlog:主从同步与恢复(追加写)
- 使命:主从同步(Replication)和数据备份恢复(比如把数据库恢复到半个月前的任意一秒)。
- 为什么追加写?
它记录的是数据库的所有历史变更逻辑。历史是绝对不能被覆盖的! 如果覆盖了,你怎么把数据库恢复到昨天下午 3 点?你怎么让新接入的从库同步全量数据?
所以,binlog 必须是一个文件写满了,就新建下一个文件,永远追加(Append),绝不覆写旧数据。
痛点二:为什么必须是“两阶段提交”?
执行一个更新事务时,InnoDB 需要写 redo log,Server 层需要写 binlog。既然要写两份日志,为什么非要搞一个复杂的“两阶段提交(2PC)”?
比如针对这样的问题:“如果不使用两阶段提交,到底会引发什么灾难?”
我们来做个逻辑推导:
假设没有两阶段提交,只有两种执行顺序:
-
方案 A:先写 redo log,再写 binlog。
- 灾难现场:redo 写完了,机器突然断电宕机,binlog 还没来得及写。
- 后果:机器重启后,InnoDB 引擎通过 redo log 把这条数据恢复了(主库有了这条新数据)。但是因为没有 binlog,这条变更永远不会同步给从库。导致主库多了一条数据,主从数据不一致。
-
方案 B:先写 binlog,再写 redo log。
- 灾难现场:binlog 写完了,机器断电宕机,redo 还没写。
- 后果:机器重启后,因为没有 redo log,主库无法恢复这条变更(主库丢失了这条数据)。但是 binlog 已经写完了,从库拿到 binlog 并执行了同步。导致从库多了一条数据,主从数据依然不一致。
结论:无论是先写谁,只要中间发生崩溃,都会导致主库和从库的数据割裂。为了解决这个问题,MySQL 必须把这两个操作绑定在一起,做到 “要么一起成功,要么一起失败”,这就是两阶段提交诞生的根本原因。
痛点三:崩溃恢复时的判定逻辑(XID 是关键)
知道了为什么要两阶段提交,那它到底是怎么工作的?在发生崩溃重启时,MySQL 又是如何精准判定一个事务该提交还是该回滚的?
这里的核心枢纽,是一个叫做 XID(事务 ID) 的东西。
1. 两阶段提交的完整执行流程
一个事务的提交,被严格拆分成了三个步骤:
- 阶段一(Prepare):InnoDB 引擎将数据写入 redo log,并将状态标记为
prepare(预提交)。此时,日志中会记录下该事务的全局唯一标识XID。 - 阶段二(Write Binlog):Server 层将变更逻辑写入 binlog。binlog 中同样会记录这个
XID。 - 阶段三(Commit):InnoDB 引擎修改 redo log 的状态,将
prepare变更为commit(真正提交)。
2. 崩溃重启后的恢复逻辑
当 MySQL 异常宕机并重启时,会去扫描 redo log,并执行以下严格的判定规则:
- 情况 1:redo log 已经是
commit状态。- 说明事务已经完美走完所有流程,直接提交,恢复数据。
- 情况 2:redo log 只有
prepare状态,没有commit。(最关键的边界情况)- 这说明系统在“写完 redo”和“最终 commit”之间崩溃了。此时,MySQL 会去磁盘提取这个 redo log 里的
XID,去 binlog 文件里寻找。 - 分支 A:如果在 binlog 里找到了对应的
XID。说明 binlog 已经成功落盘,只是最后一步没来得及改状态。为了保证主从一致,MySQL 选择提交该事务。 - 分支 B:如果在 binlog 里没找到对应的
XID。说明 binlog 都还没写完就崩了,从库也不可能收到这条变更。为了保证主从一致,MySQL 选择回滚该事务。
- 这说明系统在“写完 redo”和“最终 commit”之间崩溃了。此时,MySQL 会去磁盘提取这个 redo log 里的
小结
MySQL 的日志架构是极其精妙的工程设计:
- Redo log 用循环写保证了本机的 Crash-safe;
- Binlog 用追加写保证了集群的主从同步与时空回溯;
- 两阶段提交 与 XID 则作为坚固的桥梁,完美弥合了底层引擎与上层服务之间的缝隙,保证了整个数据库生态的绝对一致性。

863

被折叠的 条评论
为什么被折叠?



