数据库与缓存12 分钟阅读更新于 2026-07-30

MySQL 日志怎么理解:undo log、redo log、binlog 与两阶段提交

MySQL 日志章节,讲清 undo log、redo log、binlog 分别解决什么问题,为什么 redo log 和 binlog 不是同一种日志,以及两阶段提交如何避免主库恢复和从库复制不一致。

相关工具

日志不是只有一份

学到 MySQL 更新语句时,日志会一下子冒出来:undo log、redo log、binlog。很多人第一次看会觉得它们都在“记录修改”,于是分不清谁负责什么。相关的日志章节把边界讲得很清楚:undo log 是 InnoDB 存储引擎层生成的日志,用于事务回滚和 MVCC;redo log 也是 InnoDB 生成的日志,用于持久性和崩溃恢复;binlog 是 Server 层生成的日志,用于数据备份和主从复制。

这三类日志不是重复劳动。它们站在不同位置,解决不同问题。事务失败了要能撤回,靠 undo log;事务提交后机器掉电,要能把已提交修改恢复出来,靠 redo log;主从复制和按时间点恢复,需要把数据库变更以归档形式传出去,靠 binlog。

这一篇先不钻配置参数,先把三件事讲顺:更新前为什么要记旧值,提交后为什么要记新变化,为什么 Server 层还要有一份归档日志。理解这三件事,再看两阶段提交就自然了。

MySQL 三类日志与两阶段提交图,包含 undo log、redo log、binlog、redo prepare、写 binlog、redo commit 和主从一致性风险
MySQL 三类日志与两阶段提交

undo log 管回滚和版本链,redo log 管崩溃恢复,binlog 管归档和复制;两阶段提交让 redo log 与 binlog 保持一致。

undo log 记录修改前的样子

undo log 是回滚日志。文中说,当事务对数据库进行插入、修改、删除时,系统会记录相应的 undo log,以便事务回滚或系统恢复时使用。它记录的信息包括操作类型、修改前的数据值、被修改数据的位置、事务标识 ID 等。

比如更新一条记录,把 `name` 从旧值改成新值。为了能回滚,MySQL 要把旧值记下来。事务执行到一半如果报错,或者用户显式执行 `rollback`,数据库就可以根据 undo log 逆向执行,把数据还原到事务开始前的状态。资料 在事务章节说原子性靠 undo log 保证,就是这个意思。

undo log 还有另一个重要用途:MVCC。文中提到,每次更新生成的 undo log 里有 `roll_pointer` 指针和 `trx_id` 事务 ID,多个旧版本会串成版本链。普通 `select` 做快照读时,MySQL 会根据事务的 Read View,顺着 undo log 版本链找到当前事务可见的那一版记录。

redo log 记录数据页做了什么修改

redo log 是重做日志,主要服务于崩溃恢复。文中说,redo log 是物理日志,记录某个数据页做了什么修改,比如某个表空间、某个数据页、某个偏移量的位置发生了什么更新。每执行一个事务,就可能产生一条或多条物理日志。

为什么需要它?因为数据库更新不一定每次都立刻把数据页完整刷到磁盘。为了性能,MySQL 可能先修改内存里的数据页,再通过后台机制慢慢刷盘。但事务提交后,系统必须保证“已经提交的修改不能丢”。所以提交时只要先把 redo log 持久化到磁盘,崩溃后就可以用 redo log 把数据页恢复到正确状态。

资料 也对 undo log 和 redo log 做了对比:两者都是 InnoDB 存储引擎生成的日志,但 undo log 记录事务开始前的数据状态,也就是更新之前的值;redo log 记录事务完成后的数据状态,也就是更新之后对数据页造成的物理修改。提交前崩溃,可以通过 undo log 回滚;提交后崩溃,可以通过 redo log 恢复。

binlog 是 Server 层的归档日志

binlog 和 redo log 最容易混淆。文中说,MySQL 在完成一条更新操作后,Server 层还会生成一条 binlog,等事务提交时,会把事务执行过程中产生的所有 binlog 统一写入 binlog 文件。binlog 记录数据库表结构变更和表数据修改,不记录 `select`、`show` 这类查询操作。

它的定位是归档。redo log 主要给 InnoDB 做崩溃恢复,binlog 主要给备份恢复和主从复制使用。比如主库把 binlog 传给从库,从库按日志内容重放,就能跟上主库的数据变化。做数据恢复时,也可以基于备份加 binlog 回放到某个时间点附近。

资料 对二者差异也分了几层:binlog 是 Server 层实现,所有存储引擎都可以使用;redo log 是 InnoDB 实现。redo log 是物理日志,记录数据页修改;binlog 有 Statement、Row、Mixed 等格式,记录逻辑操作或行变更。redo log 循环写,空间固定;binlog 追加写,文件达到一定大小后切到下一个,不会覆盖旧日志。

为什么 redo log 和 binlog 都要有

既然 binlog 也能记录更新,为什么还要 redo log?反过来,既然 redo log 能恢复数据,为什么还要 binlog?答案还是它们位置不同、用途不同。redo log 贴近 InnoDB 的数据页,用于崩溃恢复;binlog 站在 Server 层,用于归档、复制和恢复到外部环境。

如果只有 redo log,主库自身恢复问题可以处理,但主从复制、增量备份、跨引擎归档都会缺少统一日志。如果只有 binlog,Server 层知道执行了什么逻辑操作,但 InnoDB 崩溃恢复需要的是数据页层面的物理修改信息,恢复效率和可靠性都不合适。

所以它们是配合关系,不是替代关系。一个影响主库崩溃后怎样恢复,一个影响从库和备份系统怎样重放。也正因为两个日志都很重要,它们之间的一致性才成了事务提交里的核心问题。

半成功会造成主从不一致

资料 解释两阶段提交时,先讲了两个危险场景。第一种是 redo log 已经刷到磁盘,MySQL 突然宕机,但 binlog 还没写入。重启后,主库可以通过 redo log 把 Buffer Pool 恢复到新值,可 binlog 里没有这条更新。主从架构里,从库拿不到这条更新,结果主库是新值,从库是旧值。

第二种是 binlog 已经刷到磁盘,MySQL 突然宕机,但 redo log 还没写入。重启后,因为 redo log 里没有这次修改,主库认为事务无效,数据还是旧值;但 binlog 已经记录了更新,从库复制后会执行这条更新,变成新值。这样还是主从不一致。

这两个例子说明,redo log 和 binlog 任意一方先成功、另一方失败,都可能把系统推到尴尬状态。redo log 影响主库恢复,binlog 影响从库复制和备份恢复。事务提交要保证的,不只是“本机改成功”,还包括“本机恢复和外部重放看到同一个事实”。

两阶段提交把提交拆成 prepare 和 commit

为了解决 redo log 和 binlog 的一致性问题,MySQL 使用两阶段提交。文中说,在 InnoDB 存储引擎中,如果开启 binlog,MySQL 会同时维护 binlog 和 redo log。为了保证两个日志一致,MySQL 使用内部 XA 事务,其中 binlog 作为协调者,存储引擎作为参与者。

提交流程可以分成两步。第一步是 prepare 阶段:把内部 XA 事务 ID 写入 redo log,同时把 redo log 对应事务状态设置为 prepare,然后把 redo log 持久化到磁盘。此时事务还没有对外宣告完成。

第二步是 commit 阶段:把内部 XA 事务 ID 写入 binlog,并把 binlog 持久化到磁盘,随后调用引擎提交接口,把 redo log 状态设置为 commit。文中还提到,redo log commit 状态不一定必须立刻持久化到磁盘,只要 binlog 写磁盘成功,即使 redo log 还是 prepare 状态,也可以被认为事务已经执行成功。

用一条 update 串起来看

把这些机制放回一条 `update` 语句里,会更容易理解。应用发起更新,MySQL 先走连接、解析、预处理、优化和执行流程。真正修改数据前,InnoDB 会准备 undo log,把旧值和版本链信息记下来,保证后面可以回滚,也能支持 MVCC。

执行修改时,内存里的数据页会变化,InnoDB 生成 redo log,记录数据页层面的物理修改。提交时,redo log 先进入 prepare 状态并刷盘,然后 Server 层写 binlog 并刷盘,最后 InnoDB 把 redo log 标记为 commit。这样主库崩溃恢复和从库复制才能对同一事务达成一致。

你可以把 undo log 看成“撤回按钮”,redo log 看成“崩溃后重做凭据”,binlog 看成“对外归档记录”。三者都叫日志,但服务的对象完全不同。理解这点,后面学主从复制、备份恢复、事务提交性能,就不会把它们混成一锅。

排查问题时要先判断是哪类日志

如果问题是事务回滚、长事务、快照读看到了旧版本,优先想到 undo log 和 MVCC。长事务会让旧版本迟迟不能清理,undo 压力就会上来。如果问题是机器掉电、异常重启后数据是否丢失,优先想到 redo log 和刷盘策略。

如果问题是主从延迟、从库数据不一致、按时间点恢复、误删恢复,优先想到 binlog。binlog 是否开启、格式是什么、保留多久、从库重放到哪里,都会影响恢复和复制结果。不要看到“日志”两个字就笼统排查,要先判断它服务的是回滚、崩溃恢复,还是复制归档。

面试里回答也可以按这个顺序:undo log 记录旧值,用于回滚和 MVCC;redo log 是 InnoDB 物理日志,用于崩溃恢复;binlog 是 Server 层逻辑日志,用于备份和主从复制;redo log 和 binlog 通过两阶段提交保证一致,避免主库恢复和从库复制出现不同结果。

常见问题

undo log 和 redo log 最大区别是什么?

undo log 记录修改前的旧值,用于事务回滚和 MVCC;redo log 记录数据页的物理修改,用于事务提交后的崩溃恢复。

binlog 为什么不是 redo log 的替代品?

binlog 是 Server 层归档日志,主要用于备份恢复和主从复制;redo log 是 InnoDB 的物理日志,主要用于崩溃恢复。它们层级、格式、用途都不同。

为什么需要两阶段提交?

因为 redo log 影响主库崩溃恢复,binlog 影响从库复制和备份恢复。如果两者只写成功一个,就可能造成主库和从库看到不同数据。两阶段提交用于保证二者一致。

redo log 是追加写还是循环写?

redo log 是循环写,日志空间固定,写满后会从头复用;binlog 是追加写,文件达到一定大小后切换新文件,不会覆盖旧日志。

MySQL 与 Redis

继续阅读

返回专题