跳过正文

MySQL 日志

·330 字·2 分钟
mysql - 这篇文章属于一个选集。
§ 7: 本文

MySQL三大日志是什么?
#

说出 undolog、redolog、binlog 三种日志的作用。

回答
#

  • undo log是Innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC。在事务没提交之前,Innodb会先记录更新前的数据记录 undo log中,回滚时利用 undo log 来进行回滚。
  • redo log 也是Innodb存储引擎层的日志,属于物理日志,记录了某个数据页做了什么修改,实现了事务的持久性,主要用于掉电等故障恢复。比如某个事务提交了,脏页数据还没有刷盘,如果 MySQL 机器断电了,脏页的数据就丢失了,MySQL 重启后可以通过redolog日志,可以将已提交事务的数据恢复回来。
  • binlog 是 Server 层生成的日志,主要用于数据备份和主从复制。在完成一条更新操作后,Server 层会生成一条 binlog,等之后事务提交的时候,会将该事务执行过程中产生的所有 binlog 统一写入 binlog 文件。binlog 文件是记录了所有数据库表结构变更和表数据修改的日志,不会记录查询类的操作。

redo log 和 binlog 的区别和应用场景?
#

redo log 和 binlog 有4个区别的地方。

适用对象不同:

  • binlog 是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用;
  • redo log 是 Innodb 存储引擎实现的日志;

文件格式不同:

  • binlog 有 3 种格式类型,分别是 STATEMENT(默认格式)、ROW、 MIXED,区别如下:
    • STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中(相当于记录了逻辑操作,所以针对这种格式, binlog 可以称为逻辑日志),主从复制中 slave 端再根据 SQL 语句重现。但 STATEMENT 有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;
    • ROW:记录行数据最终被修改成什么样了(这种格式的日志,就不能称为逻辑日志了),不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已;
    • MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
  • redo log 是物理日志,记录的是在某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新;

写入方式不同:

  • binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志。
  • redo log 是循环写,日志空间大小是固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志。

用途不同:

  • binlog 用于备份恢复、主从复制;
  • redo log 用于掉电等故障恢复。

面试的时候,重点说出两个日志的内容和应用场景的区别。

回答
#

  • redo log 是 InnoDB 引擎实现的日志,属于物理日志,记录了 Innodb 存储引擎对数据页所做的修改操作,主要用于崩溃恢复,比如某个事务提交了,脏页数据还没有刷盘,如果 MySQL 机器断电了,脏页的数据就丢失了,MySQL 重启后可以通过重做日志,可以将已提交事务的数据恢复回来。
  • binlog 是 server 层实现的日志,保存了所有对数据库的增删改操作,binlog 有三种日志格式,日志的内容可能是 SQL 语句、数据本身或两者的混合,主要用于数据库备份和归档,也用于主从复制。

redo log 和 binlog 在恢复数据库有什么区别?
#

考察 redo log 和 binlog 应用区别。

回答
#

  • binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存了所有对数据库的更新操作,可以用来恢复数据库某个时刻的数据或者全量恢复数据库数据。
  • redo log 是循环写,日志空间大小是固定,全部写满就从头开始,保存的是 Innodb 存储引擎对数据页所做的修改操作,用来恢复因中途 MySQL 断电丢失的脏页数据。

为什么崩溃恢复不用binlog 而用redolog?
#

回答
#

binlog 是 server 层的日志,不会记录 innodb 存储引擎层中有哪些数据页没有被刷盘,redolog 是 innodb 层的日志,可以记录哪些脏页没有被刷盘,崩溃恢复的时候,恢复的粒度更细粒,可以精确到需要恢复的数据页,而 binlog 保存的是全量日志,没办法做到这一点,所以崩溃恢复用的是redolog

redo log 是怎么实现持久化的?
#

  • 先说明没有 redo log 前,数据库发生宕机时,脏页数据可能会发生丢失的问题。
  • 再说明引入了 redo log 后,是怎么实现持久化的。

回答
#

事务执行过程更新的数据,并不是在事务提交的时候,就把修改的数据刷入磁盘的,而是修改 buffer pool 中数据页,并标记为脏页,然后后台再找合适的时间刷盘。

如果事务提交了,脏页数据没有刷盘时,数据库发生宕机,这就会导致事务修改的数据丢失了。

所以 MySQL 就引入了 redo log, redo log 保存的内容是物理日志,主要是记录Innodb对某个数据页的修改操作,当事务提交的时候,redo log 会先刷入磁盘,因为 redo log 保存了数据页的修改操作,即使脏页数据没有刷盘时数据库发生宕机了,重启后 MySQL 通过重放 redo log ,就能恢复未刷盘的脏页,保证了数据的持久化。

redo log除了崩溃恢复还有什么其他作用?
#

写入 redo log 的方式使用了追加操作, 所以磁盘操作是顺序写,而写入数据需要先找到写入位置,然后才写到磁盘,所以磁盘操作是随机写。

磁盘的「顺序写 」比「随机写」 高效的多,因此 redo log 写入磁盘的开销更小。针对「顺序写」为什么比「随机写」更快这个问题,可以比喻为你有一个本子,按照顺序一页一页写肯定比写一个字都要找到对应页写快得多。

可以说这是 WAL 技术的另外一个优点:MySQL 的写操作从磁盘的「随机写」变成了「顺序写」,提升语句的执行性能。这是因为 MySQL 的写操作并不是立刻更新到磁盘上,而是先记录在日志上,然后在合适的时间再更新到磁盘上。

针对为什么需要 redo log 这个问题我们有两个答案:

  • 实现事务的持久性,让 MySQL 有 crash-safe 的能力,能够保证 MySQL 在任何时间段突然崩溃,重启后之前已提交的记录都不会丢失;
  • 将写操作从「随机写」变成了「顺序写」,提升 MySQL 写入磁盘的性能。

回答
#

写 Redolog 日志是追加的形式,所以 redolog 写磁盘是一个顺序写的过程,而数据页写磁盘是一个随机写的过程,顺序写的性能是比随机写性能高的,事务在提交的时候,是先写日志再写数据的机制,相当于把 MySQL 写入磁盘的操作从磁盘随机写成了顺序写,所以 redo log 还可以起到提升 MySQL 写入磁盘性能的作用。

为什么需要两阶段提交?
#

回答
#

两阶段提交是为了保证 redo log 和 binlog 逻辑一致,从而保证主从复制的时候不会出现数据不一致的问题。

事务提交后,redo log 和 binlog 都要持久化到磁盘,但是这两个是独立的逻辑,可能出现半成功的状态,比如在主从复制的场景下,如果在将 redo log 刷入到磁盘之后, MySQL 突然宕机了,而 binlog 还没有来得及写入磁盘,这时候主库是最新的数据,而从库是旧数据,这样就造成两份日志之间的逻辑不一致。

两阶段提交的过程?
#

image-20260503005919009

回答
#

两阶段提交把事务的提交拆分成了 2 个阶段,分别是准备阶段和提交阶段。

  • 准备阶段会将 redo log 状态设置为 prepare 状态,然后将 redo log 刷入磁盘;
  • 提交阶段会将 binlog 刷入磁盘,然后设置 redo log 设置为 commit 状态,到这里两阶段就已经完成了。

在两阶段提交中,是以 binlog 刷入磁盘时机作为事务提交成功的标志的:

  • 如果 binlog 还没刷入磁盘的时候,MySQL 就发生了崩溃,MySQL 重启的时候就需要回滚事务;
  • 如果 binlog 刷入磁盘,即使 redo log 没有设置 commit 状态,MySQL 就发生了崩溃,MySQL 重启的时候就会提交事务。
凉柠
作者
凉柠
专注于 Kubernetes、分布式系统与 AI Agent 架构探索。
mysql - 这篇文章属于一个选集。
§ 7: 本文