跳过正文
  1. 面试题库/

07|日志

·4556 字·10 分钟
目录
MySQL面试题库 - 这篇文章属于一个选集。
§ 7: 本文

1. MySQL 三大日志是什么?(重要)
#

分析

说出 undo logredo logbinlog 三种日志的作用。

回答

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

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

分析

redo logbinlog 有 4 个区别。

适用对象不同:

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

文件格式不同:

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

写入方式不同:

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

用途不同:

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

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

回答

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

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

分析

考察 redo logbinlog 应用区别。

回答

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

4. 为什么崩溃恢复不用 binlog 而用 redo log?
#

分析

binlog 是 Server 层日志,不会记录 InnoDB 存储引擎层哪些脏页没有被刷盘;redo log 是 InnoDB 层日志,可以记录哪些脏页没有被刷盘。

回答

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

5. binlog 的三种格式是什么?
#

分析

分别说出 binlogSTATEMENTROWMIXED 格式的特点即可。

回答

binlog 有 3 种格式类型,分别是 STATEMENT(默认格式)、ROWMIXED,区别如下:

  • STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中,主从复制中 slave 端再根据 SQL 语句重现。缺陷:STATEMENT 有动态函数的问题,比如用了 uuid 或者 now 这些函数,在主库上执行的结果并不是在从库执行的结果,这种随时在变的函数会导致复制的数据不一致。
  • ROW:记录行数据最终被修改成什么样了,不会出现 STATEMENT 下动态函数的问题。缺陷:ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句。
  • MIXED:包含了 STATEMENTROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式。

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

分析

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

回答

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

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

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

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

分析

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

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

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

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

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

回答

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

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

分析

先说结论,再分析没有两阶段提交会有什么问题。

回答

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

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

9. 两阶段提交的过程?
#

分析

alt text

回答

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

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

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

  • 如果 binlog 还没刷入磁盘的时候,MySQL 就发生了崩溃,MySQL 重启的时候就需要回滚事务;
  • 如果 binlog 刷入磁盘,即使 redo log 没有设置 commit 状态,MySQL 就发生了崩溃,MySQL 重启的时候就会提交事务。

10. redo log 刷盘策略有哪三种?
#

分析

单独执行一个更新语句的时候,InnoDB 引擎会自己启动一个事务,在执行更新语句的过程中,生成的 redo log 先写入到 redo log buffer 中,然后等事务提交的时候,再将缓存在 redo log buffer 中的 redo log 按组的方式「顺序写」到磁盘。

上面这种 redo log 刷盘时机是在事务提交的时候,这个默认的行为。除此之外,InnoDB 还提供了另外两种策略,由参数 innodb_flush_log_at_trx_commit 控制,可取的值有:012,默认值为 1,这三个值分别代表的策略如下:

  • 当设置该参数为 0 时,表示每次事务提交时,还是将 redo log 留在 redo log buffer 中,该模式下在事务提交时不会主动触发写入磁盘的操作。
  • 当设置该参数为 1 时,表示每次事务提交时,都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘,这样可以保证 MySQL 异常重启之后数据不会丢失。
  • 当设置该参数为 2 时,表示每次事务提交时,都只是缓存在 redo log buffer 里的 redo log 写到 redo log 文件。注意写入到「redo log 文件」并不意味着写入到了「磁盘」,因为操作系统的文件系统中有个 Page Cache,Page Cache 是专门用来缓存文件数据的,所以写入「redo log 文件」意味着写入到了操作系统的文件缓存。

以下图方便理解:

alt text

innodb_flush_log_at_trx_commit02 的时候,什么时候才将 redo log 写入磁盘?

InnoDB 的后台线程每隔 1 秒:

  • 针对参数 0:会把缓存在 redo log buffer 中的 redo log,通过调用 write() 写到操作系统的 Page Cache,然后调用 fsync() 持久化到磁盘。所以参数为 0 的策略,MySQL 进程的崩溃会导致上一秒钟所有事务数据的丢失;
  • 针对参数 2:调用 fsync(),将缓存在操作系统中 Page Cache 里的 redo log 持久化到磁盘。所以参数为 2 的策略,较取值为 0 情况下更安全,因为 MySQL 进程的崩溃并不会丢失数据,只有在操作系统崩溃或者系统断电的情况下,上一秒钟所有事务数据才可能丢失。

加入了后台线程后,innodb_flush_log_at_trx_commit 的刷盘时机如下图:

alt text

这三个参数的数据安全性和写入性能的比较如下:

  • 数据安全性:参数 1 > 参数 2 > 参数 0
  • 写入性能:参数 0 > 参数 2 > 参数 1

所以,数据安全性和写入性能是熊掌不可得兼的,要不追求数据安全性,牺牲性能;要不追求性能,牺牲数据安全性。

  • 在一些对数据安全性要求比较高的场景中,显然 innodb_flush_log_at_trx_commit 参数需要设置为 1
  • 在一些可以容忍数据库崩溃时丢失 1s 数据的场景中,我们可以将该值设置为 0,这样可以明显地减少日志同步到磁盘的 I/O 操作。
  • 安全性和性能折中的方案就是参数 2,虽然参数 2 没有参数 0 的性能高,但是数据安全性方面比参数 0 强,因为参数 2 只要操作系统不宕机,即使数据库崩溃了,也不会丢失数据,同时性能方便比参数 1 高。

回答

redo log 刷盘策略主要有三种:

  • 当刷盘策略配置为参数 0 的时候,表示每次事务提交时,还是将 redo log 留在 redo log buffer 中,该模式下在事务提交时不会主动触发写入磁盘的操作,后续由 InnoDB 后台线程把缓存在 redo log buffer 中的 redo log,写入到操作系统 Page Cache 缓存并持久化到磁盘。
  • 当刷盘策略配置为参数 1 的时候,表示每次事务提交时,都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘。
  • 当刷盘策略配置为参数 2 的时候,表示每次事务提交时,都只是缓存在 redo log buffer 里的 redo log 写到操作系统的 Page Cache 缓存,但是并不会执行刷盘操作,后续由 InnoDB 后台线程来执行刷盘操作。

这三种刷盘策略,参数 1 的模式是数据安全性最高的,但是也是写入性能最差的;而参数 0 是数据安全性最差的,但是写入性能最好的。

MySQL面试题库 - 这篇文章属于一个选集。
§ 7: 本文