深入浅出 MySQL 三大日志:底层原理与数据安全

44 阅读4分钟

在 MySQL 中,数据的读写并不是直接对着磁盘干,而是通过一套复杂的日志系统来保证性能安全。如果你执行一条 UPDATE 语句,虽然你没看到磁盘在狂转,但后台已经经历了一场关于三大日志的“接力赛”。

一、 为什么不能直接写磁盘?(Buffer Pool 与 WAL)

在理解日志之前,我们要先看 Buffer Pool(缓冲池)。

  • 痛点:磁盘随机读写非常慢。如果每次改数据都直接写磁盘,数据库的并发量根本上不去。
  • 方案:MySQL 会先把数据读到内存里的 Buffer Pool 中。修改时,也是先改内存。
  • 新问题:万一内存改完,还没来得及同步到磁盘就断电了,数据不就丢了吗?

为了解决这个问题,MySQL 引入了 WAL 机制 (Write-Ahead Logging) ,即:先写日志,再写磁盘。而这个日志,就是我们今天要说的第一个主角——Redo Log


二、 三大日志各司其职

我们可以把数据库比作一个大型超市的收银系统

1. Undo Log(回滚日志):事务的“后悔药”

  • 比喻:相当于操作记录的“撤销”键。

  • 作用

    • 原子性:如果你改数据改到一半事务失败了,Undo Log 记录了你改之前长什么样,直接帮你“逆向操作”回去。
    • MVCC(多版本并发控制) :它保存了数据的历史版本,让其他事务能看到改之前的数据(快照读)。
  • 触发:只要事务开始修改,就会先生成 Undo Log。

2. Redo Log(重做日志):崩溃恢复的“保险”

  • 比喻:收银员手里的“临时小账本”。

  • 特性

    • 物理日志:记录的是“在某个数据页的某个位置改了什么”。
    • 顺序写:日志是追加写入的,比随机找磁盘位置快得多。
  • 核心作用:保证 Crash-Safe(崩溃安全)。即使数据库宕机,重启后只要看一眼 Redo Log,就能把 Buffer Pool 里没来得及刷盘的数据恢复出来。

3. Binlog(归档日志):集群同步的“全量录像”

  • 比喻:超市总部的“全天交易流水明细”。

  • 特性

    • 逻辑日志:记录的是 SQL 语句或行数据的变更(比如:把 ID=1 的余额加 100)。
    • 全量保存:Redo Log 是循环覆盖写的(旧的会被删),而 Binlog 是追加写的,保存了数据库所有历史操作。
  • 核心作用主从复制数据恢复(找回误删的数据)。


三、 深度解析:两阶段提交(2PC)

这是面试最常问的点:既然有了 Redo Log 保证安全,为什么还要 Binlog?为什么写日志还要分两步?

如果 Redo Log 记录了“已修改”,但 Binlog 还没来得及记,主库重启恢复了,但从库通过 Binlog 同步时就会少这一条数据,导致主从不一致

为了解决这个问题,MySQL 使用了“两阶段提交”:

  1. Prepare 阶段

    • 执行器先改内存,然后 InnoDB 引擎将修改记录写入 Redo Log,并将状态标记为 prepare(准备就绪)。
  2. 写 Binlog

    • 执行器将操作写入 Binlog,并刷到磁盘。
  3. Commit 阶段

    • 执行器调用引擎的提交事务接口,将 Redo Log 的状态改为 commit(已提交)。

异常场景模拟:

  • 如果在第 2 步写 Binlog 之前宕机:重启后发现 Redo Log 只是 prepare,且 Binlog 里没这条记录,直接回滚
  • 如果在第 2 步写完 Binlog 之后宕机:重启后发现虽然 Redo Log 是 prepare,但 Binlog 里已经有记录了。此时认为事务有效,自动提交

结论:两阶段提交确保了 Redo Log 和 Binlog 在逻辑上是原子性的,保证了库里数据和同步出去的数据一模一样。


四、 总结

特性Undo LogRedo LogBinlog
层级InnoDB 引擎层InnoDB 引擎层MySQL Server 层
记录内容逻辑操作(逆向SQL)物理页修改逻辑操作(SQL/行记录)
主要目的事务回滚 (ACID-A)崩溃恢复 (Crash-Safe)主从复制、全量恢复
写入时机修改前持续写入(Prepare/Commit)事务提交时