MySQL 中 redo log、undo log、binlog 的区别与协作流程

在 MySQL 的面试中,关于日志系统的问题几乎是必考项,尤其是 redo log、undo log 和 binlog 这三者之间的区别与协作流程。很多候选人对单个日志的概念能说出一二,但一旦被问到“一次更新语句执行时,这三个日志是如何配合工作的”,往往就答不完整。本文将从原理出发,系统梳理这三种日志的定位、写入机制以及它们在一次事务提交中的协作流程。

一、三种日志的定位与核心区别

在深入协作流程之前,先明确每种日志“为什么存在”。

1. redo log(重做日志)

redo log 是 InnoDB 存储引擎特有的物理日志,记录的是“在某个数据页上做了什么修改”。它的核心作用是保证事务的持久性,同时通过 WAL(Write-Ahead Logging)机制提升写入性能。

  • 所属层:InnoDB 存储引擎层。
  • 日志类型:物理日志,记录数据页的物理变更。
  • 写入方式:循环写,固定大小(由 innodb_log_file_sizeinnodb_log_files_in_group 控制),写满后覆盖最旧的部分。
  • 核心作用:崩溃恢复。即使脏页还没刷盘,只要 redo log 落盘,重启后就能恢复数据。

2. undo log(回滚日志)

undo log 同样属于 InnoDB 引擎层,记录的是数据的“逻辑反向操作”。比如执行一条 INSERT,undo log 中会记录一条对应的 DELETE;执行 UPDATE,会记录反向的 UPDATE。

  • 所属层:InnoDB 存储引擎层。
  • 日志类型:逻辑日志,记录反向操作。
  • 写入方式:随事务产生,存储在 undo 表空间中。
  • 核心作用:事务回滚和 MVCC(多版本并发控制)。回滚时执行反向操作;MVCC 中通过 undo log 找到数据的历史版本,实现一致性读。

3. binlog(二进制日志)

binlog 是 MySQL Server 层实现的逻辑日志,记录的是“数据库执行的所有修改操作”,比如一条 UPDATE 语句或一行数据的变化。

  • 所属层:MySQL Server 层,所有存储引擎共用。
  • 日志类型:逻辑日志,记录 SQL 语句或行变更。
  • 写入方式:追加写,文件写满后切换到新文件,不会覆盖。
  • 核心作用:主从复制和数据恢复(如 point-in-time recovery)。

三者核心区别对比

维度 redo log undo log binlog
所属层 InnoDB 引擎层 InnoDB 引擎层 Server 层
日志类型 物理日志 逻辑日志(反向) 逻辑日志
写入方式 循环写 随事务写 追加写
主要作用 持久性、崩溃恢复 回滚、MVCC 主从复制、归档恢复
是否可关闭 不可 不可 可(skip-log-bin

二、两阶段提交与协作流程

理解了三种日志的定位后,最关键的问题是:一次 UPDATE 语句执行时,它们是如何协作的?

这里涉及 MySQL 保证数据一致性的核心机制——两阶段提交(2PC)。之所以需要两阶段提交,是因为 redo log 和 binlog 分属不同层,如果两者写入不一致,会导致主从数据不一致或崩溃恢复后数据错乱。

一次 UPDATE 语句的完整执行流程

假设执行 UPDATE user SET name = 'Tom' WHERE id = 1;,流程如下:

第一阶段:准备阶段(Prepare)

  1. 执行器调用 InnoDB 引擎接口,获取 id=1 这一行数据。如果数据页在内存中就直接返回,否则从磁盘读入内存。
  2. 写入 undo log:记录修改前的旧值,用于回滚和 MVCC。
  3. 更新内存中的数据页:将 name 改为 'Tom',此时数据页成为脏页。
  4. 写入 redo log:将修改记录到 redo log buffer,此时 redo log 处于 prepare 状态
  5. 写入 binlog:执行器将修改操作写入 binlog,并刷盘(受 sync_binlog 控制)。

第二阶段:提交阶段(Commit)

  1. 提交事务:redo log 的状态从 prepare 改为 commit
  2. 后台刷脏:InnoDB 的后台线程在合适时机将脏页刷入磁盘。

为什么需要两阶段提交?

关键在于 redo log 的 prepare 和 commit 两个状态。假设不用两阶段提交,而是先写 redo log 再写 binlog,或者反过来,会出现什么问题?

场景一:先写 redo log,再写 binlog,binlog 写入前崩溃。

  • 重启后,redo log 已落盘,InnoDB 认为事务已提交,数据恢复为新值。
  • 但 binlog 没有记录这条修改,从库不会同步这条数据。
  • 结果:主库和从库数据不一致。

场景二:先写 binlog,再写 redo log,redo log 写入前崩溃。

  • 重启后,redo log 没有记录,InnoDB 认为事务未提交,回滚。
  • 但 binlog 已经写入,从库会同步这条修改。
  • 结果:主库回滚了,从库却执行了,数据不一致。

两阶段提交的解决方式:

  • 崩溃恢复时,如果 redo log 处于 prepare 状态,则去检查对应的 binlog 是否完整。
    • 如果 binlog 完整,则提交事务(redo log 改为 commit)。
    • 如果 binlog 不完整,则回滚事务。
  • 如果 redo log 已经是 commit 状态,则直接提交。

这样就保证了 redo log 和 binlog 的逻辑一致性,无论在哪一步崩溃,主从数据都能保持一致。

三、常见面试追问

追问 1:redo log 和 binlog 都能恢复数据,有什么区别?

redo log 是物理日志,循环写,用于崩溃恢复,保证的是“已提交事务不丢失”;binlog 是逻辑日志,追加写,用于归档和主从复制,可以恢复到任意时间点。redo log 空间有限,binlog 可以长期保存。

追问 2:undo log 什么时候删除?

undo log 在事务提交后不会立即删除。因为 MVCC 可能需要它来提供历史版本,只有当没有更早的 Read View 需要用到该 undo log 时,才会由 purge 线程清理。

追问 3:为什么 redo log 要采用循环写?

因为 redo log 的核心目标是崩溃恢复,只需要保证“脏页刷盘前,对应的 redo log 还在”即可。一旦脏页刷盘,对应的 redo log 就可以被覆盖。循环写节省空间,同时顺序写入性能高。

追问 4:binlog 的三种格式有什么区别?

  • STATEMENT:记录 SQL 语句,日志量小,但某些函数(如 NOW())可能导致主从不一致。
  • ROW:记录每行数据的变更,日志量大,但主从一致性强,是当前推荐格式。
  • MIXED:混合模式,普通语句用 STATEMENT,可能不一致的用 ROW。

四、总结

redo log、undo log 和 binlog 是 MySQL 保证数据可靠性、一致性和可恢复性的三驾马车:

  • redo log 保证持久性,让已提交的事务不丢失;
  • undo log 保证原子性和 MVCC,支持回滚和一致性读;
  • binlog 保证可复制性和归档恢复,支撑主从架构。

两阶段提交是连接 redo log 和 binlog 的桥梁,通过 prepare 和 commit 两个阶段,确保两者逻辑一致,从而保证主从数据一致和崩溃恢复的正确性。理解这套协作流程,不仅能在面试中脱颖而出,也能在实际排查数据不一致问题时提供清晰的思路。

未经允许不得转载:任鹏个人博客 » MySQL 中 redo log、undo log、binlog 的区别与协作流程

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏