少女祈祷中...
NEKO
知识库

mysql 日志面试要点

一、日志总览类

Q1:MySQL 有哪些日志?各自作用是什么?

日志

层次

类型

作用

redo log

InnoDB 引擎层

物理日志

崩溃恢复、保证持久性

undo log

InnoDB 引擎层

逻辑日志

事务回滚、MVCC 版本链

binlog

Server 层

逻辑日志

主从复制、数据归档、恢复

error log

Server 层

文本

错误、启动关闭信息

slow query log

Server 层

文本

慢查询分析

general log

Server 层

文本

记录所有请求,调试用

面试重点通常是前三个。

Q2:redo log、undo log、binlog 的核心区别?

维度

redo log

undo log

binlog

层次

InnoDB

InnoDB

Server

类型

物理

逻辑

逻辑

内容

页的物理修改

反向操作

SQL 或行变更

写入

循环写

追加

追加

用途

崩溃恢复

回滚、MVCC

复制、归档

是否可关

不能

不能

可以

二、redo log 类

Q3:redo log 是什么?为什么需要它?

  • 物理日志,记录“在某个数据页上做了什么修改”。

  • 作用:WAL(Write-Ahead Logging),先写日志再刷脏页,保证崩溃后能恢复已提交事务。

  • 没有它,每次提交都要随机写磁盘数据页,性能极差。

Q4:什么是 WAL?为什么能提升性能?

  • Write-Ahead Logging:修改先写 redo log(顺序写),再择机刷数据页(随机写)。

  • 顺序写远快于随机写,且日志体积小,刷盘快。

  • 崩溃时用 redo log 重放,恢复数据。

Q5:redo log 的组成和循环写机制?

  • 由多个固定大小文件组成,如 ib_logfile0、ib_logfile1。

  • 循环写:write pos 写位置,checkpoint 可擦除位置。

  • write pos 追上 checkpoint 时,必须停下来刷脏页,腾出空间。

Q6:redo log 什么时候刷盘?

由 innodb_flush_log_at_trx_commit 控制:

  • =1:每次提交都刷盘,最安全,默认。

  • =0:每秒刷一次,崩溃可能丢 1 秒。

  • =2:每次提交写 OS cache,每秒刷盘,宕机可能丢。

Q7:redo log 和 binlog 的写入顺序?

  • 两阶段提交:redo prepare → 写 binlog → redo commit。

  • 保证两份日志逻辑一致。

三、undo log 类

Q8:undo log 是什么?有什么作用?

  • 逻辑日志,记录数据的“反向操作”,如 INSERT 对应 DELETE。

  • 两大作用:

    1. 事务回滚:原子性。

    2. MVCC:版本链,供快照读找历史版本。

Q9:undo log 和 MVCC 的关系?

  • 每行有 roll_pointer 指向 undo log 中的旧版本。

  • 多个版本串成版本链。

  • 快照读时沿版本链找对 ReadView 可见的版本。

Q10:undo log 什么时候删除?

  • 事务提交后不一定立即删。

  • 还要供 MVCC 读旧版本,等没有 ReadView 需要它时,由 purge 线程清理。

Q11:undo log 是物理还是逻辑日志?

  • 逻辑日志。记录的是“反向 SQL 操作”,不是页的物理修改。

  • 但回滚时可能涉及多个页,所以是逻辑层面的。

四、binlog 类

Q12:binlog 是什么?有什么作用?

  • Server 层逻辑日志,记录所有变更。

  • 作用:主从复制、数据恢复、归档。

  • 与存储引擎无关,InnoDB、MyISAM 都有。

Q13:binlog 的三种格式?

格式

内容

优点

缺点

STATEMENT

SQL 语句

日志小

函数、随机可能不一致

ROW

行变更前后值

精确、安全

日志大

MIXED

混合

兼顾

复杂

  • 生产推荐 ROW,主从一致性强。

Q14:binlog 什么时候写入?

  • 事务提交时写入,先写 binlog cache,再刷盘。

  • sync_binlog=1 每次提交刷盘,最安全。

  • sync_binlog=0 由 OS 决定,可能丢。

Q15:binlog 和 redo log 的区别?

  • 层次:binlog 在 Server,redo 在 InnoDB。

  • 类型:binlog 逻辑,redo 物理。

  • 写入:binlog 追加,redo 循环写。

  • 用途:binlog 复制归档,redo 崩溃恢复。

Q16:为什么 binlog 不能做崩溃恢复?

  • binlog 是逻辑日志,不记录页的物理状态,重放成本高且不精确。

  • 崩溃恢复需要 redo log 的物理重放,快速且确定。

五、两阶段提交类

Q17:什么是两阶段提交?为什么需要?

  • 流程:写 redo(prepare)→ 写 binlog → 提交 redo(commit)。

  • 目的:保证 redo log 和 binlog 逻辑一致。

  • 不用的话,崩溃时可能出现:

    • redo 有、binlog 没有 → 主库有数据,从库没有。

    • binlog 有、redo 没有 → 从库有数据,主库没有。

背景:两份日志,两个层次

MySQL 里一次数据修改会涉及两份日志:

日志

层次

作用

redo log

InnoDB 引擎层

崩溃恢复、持久性

binlog

Server 层

主从复制、归档

问题在于:这两份日志必须逻辑一致。否则会出现:

  • redo 有、binlog 没有:主库恢复后有这条数据,但从库没收到,主从不一致。

  • binlog 有、redo 没有:从库重放后有这条数据,但主库没有,主从不一致。

如果不用两阶段提交

假设简单按“先写 redo,再写 binlog”:

  • 写完 redo 后宕机,binlog 没写 → 主库有、从库没有。

  • 先写 binlog,再写 redo,写完 binlog 宕机 → 从库有、主库没有。

无论哪种顺序,崩溃点都可能导致两份日志不一致,进而主从不一致。

两阶段提交的流程

以一条 UPDATE 为例,核心顺序是:

1. 写 redo log,标记为 prepare 状态
2. 写 binlog
3. 写 redo log,标记为 commit 状态

拆开看:

阶段一:Prepare

  • InnoDB 把修改写入 redo log,但不标记 commit,而是标记 prepare。

  • 此时事务还未真正提交,数据页也没刷盘。

阶段二:Commit

  • Server 层写 binlog,并刷盘(受 sync_binlog 控制)。

  • binlog 写成功后,InnoDB 把 redo log 的状态从 prepare 改为 commit。

  • 事务提交完成。

关键点:binlog 写在 redo prepare 之后、redo commit 之前。这个顺序是整个协议的核心。

崩溃恢复时怎么判断

崩溃后重启,InnoDB 扫描 redo log,遇到 prepare 状态的事务时,会去检查对应的 binlog:

redo 状态

binlog 状态

处理

prepare

binlog 完整

提交(redo 改 commit)

prepare

binlog 不完整/不存在

回滚

commit

—

直接恢复

判断依据:

  • binlog 完整:说明 binlog 已成功落盘,从库能收到,主库必须提交,否则主从缺这条。

  • binlog 不完整:说明 binlog 没写完,从库不会有这条,主库回滚,保持一致。

这样无论崩溃发生在哪个点,主库和从库的最终状态都一致。

为什么这个顺序能保证一致

可以用反证法理解:

为什么 binlog 必须在 redo commit 之前?

  • 如果先 redo commit,再写 binlog:

    • redo commit 后宕机,binlog 没写 → 主库有、从库没有,不一致。

为什么 binlog 必须在 redo prepare 之后?

  • 如果先写 binlog,再写 redo prepare:

    • binlog 写完后宕机,redo 还没 prepare → 从库有、主库没有,不一致。

所以顺序必须是:redo prepare → binlog → redo commit。prepare 是“占位”,commit 是“确认”,中间夹着 binlog,形成可判断的中间状态。

与组提交的关系

两阶段提交每次都要刷盘,性能有开销。MySQL 用组提交(Group Commit)优化:

  • 多个事务的 redo 和 binlog 合并成一次 fsync。

  • 分为三个阶段:

    1. Flush 阶段:把 redo 从 Log Buffer 写到文件。

    2. Sync 阶段:fsync 刷盘。

    3. Commit 阶段:写 binlog 并提交 redo。

组提交让多个事务共享一次刷盘,显著降低 fsync 次数,提升并发。

组提交不会破坏 innodb_flush_log_at_trx_commit=1 和 sync_binlog=1 的语义。它合并的是“多个事务的刷盘动作”,不是“把某个事务的刷盘推迟到配置之外”。每个事务在返回客户端之前,其 redo 和 binlog 都已经被 fsync 落盘。

MySQL 的组提交把提交过程分成三个阶段,多个事务在这三个阶段里排队、成组:

阶段 1:Flush

  • 多个事务的 redo log 从 Log Buffer 写入 redo 文件(write,不 fsync)。

  • 这些事务组成一个组。

阶段 2:Sync

  • 对这一组的 redo 文件执行一次 fsync。

  • 这一组里所有事务的 redo 都随之落盘。

  • 同时,这一组事务的 binlog 也从 binlog cache 写入 binlog 文件。

阶段 3:Commit

  • 对 binlog 执行 fsync(sync_binlog=1 时)。

  • 然后把这一组事务的 redo 状态改为 commit。

  • 组内事务依次返回客户端。

用图表示:

事务 T1, T2, T3 同时提交
        │
        ▼
[Flush]  T1/T2/T3 的 redo 写入文件
        │
        ▼
[Sync]   一次 fsync → T1/T2/T3 的 redo 全部落盘
        │
        ▼
[Commit] 写 binlog → 一次 fsync → redo 改 commit → 返回客户端

一次 fsync 覆盖了三个事务,而不是三次 fsync。

相关参数

参数

作用

innodb_flush_log_at_trx_commit

redo 刷盘策略,=1 最安全

sync_binlog

binlog 刷盘策略,=1 最安全

binlog_group_commit_sync_delay

组提交延迟,攒更多事务

binlog_group_commit_sync_no_delay_count

攒够多少个就刷

要求不丢数据:两个都设 1。

常见追问

Q:两阶段提交能保证绝对不丢吗?

  • 保证的是 redo 和 binlog 一致,不是绝对不丢。

  • 不丢还依赖 innodb_flush_log_at_trx_commit=1 和 sync_binlog=1。

Q:binlog 写完但 redo 没 commit,宕机后提交还是回滚?

  • 提交。因为 binlog 完整,从库会有这条,主库必须提交保持一致。

Q:redo prepare 后宕机,binlog 没写,怎么办?

  • 回滚。binlog 不完整,从库不会有,主库也回滚。

Q:为什么不让 binlog 也由 InnoDB 管?

  • binlog 属于 Server 层,要支持多种存储引擎,且用于复制,不能绑死在 InnoDB。

Q:组提交会破坏一致性吗?

  • 不会。组提交只是合并刷盘,逻辑顺序仍是 prepare → binlog → commit。

Q18:崩溃恢复时怎么判断提交还是回滚?

  • redo 处于 prepare:

    • binlog 完整 → 提交。

    • binlog 不完整 → 回滚。

  • redo 已 commit → 直接恢复。

Q19:两阶段提交有什么缺点?

  • 增加一次网络/磁盘交互,延迟略高。

  • 组提交(group commit)可缓解:多个事务一起刷盘。

Q20:组提交是什么?

  • 多个事务的 redo/binlog 合并成一次刷盘,减少 fsync 次数。

  • 分为 flush、sync、commit 三个阶段。

六、日志与持久性类

Q21:如何保证事务不丢?

  • innodb_flush_log_at_trx_commit=1

  • sync_binlog=1

  • 两者都设 1,每次提交都刷盘。

  • 配合半同步复制、多副本。

Q22:innodb_flush_log_at_trx_commit 三个值区别?

值

行为

安全性

0

每秒刷

可能丢 1 秒

1

每次提交刷

不丢

2

提交写 OS cache,每秒刷

宕机可能丢

Q23:redo log 写满会怎样?

  • write pos 追上 checkpoint,必须刷脏页推进 checkpoint。

  • 此时写入会阻塞,直到腾出空间。

  • 所以 redo 太小会导致频繁刷盘、性能抖动。

Q24:redo log 能设置多大?

  • innodb_log_file_size 和 innodb_log_files_in_group。

  • 太小:频繁 checkpoint,性能差。

  • 太大:崩溃恢复慢。

  • 一般几 GB 级别,按写入量调。

七、实战与排查类

Q25:如何查看 binlog?

  • SHOW BINARY LOGS;

  • SHOW BINLOG EVENTS IN 'mysql-bin.000001';

  • mysqlbinlog mysql-bin.000001

Q26:如何用 binlog 恢复数据?

  • 全量备份 + 重放 binlog 到指定时间点。

  • mysqlbinlog --start-datetime --stop-datetime ... | mysql -u ...

Q27:主从复制中 binlog 怎么流转?

  1. 主库写 binlog。

  2. dump 线程发给从库。

  3. 从库 IO 线程写 relay log。

  4. SQL 线程重放 relay log。

Q28:redo log 和 binlog 不一致会怎样?

  • 主从数据不一致,主库有从库没有,或反之。

  • 两阶段提交就是为避免这个。

Q29:undo log 太大有什么问题?

  • 长事务导致版本链长,占用空间。

  • 影响 MVCC 查询性能,purge 压力大。

  • 应避免长事务。

Q30:binlog 格式怎么选?

  • 生产推荐 ROW:精确、安全,主从一致。

  • STATEMENT 日志小但有函数/随机问题。

  • MIXED 折中,但仍有风险。

八、高频陷阱题

Q31:redo log 是事务提交就写数据页吗?

  • 不是。提交只保证 redo 刷盘,数据页由后台线程择机刷。

  • 这是 WAL 的核心:先日志后数据。

Q32:binlog 是事务提交前还是后写?

  • 提交过程中写,在 redo prepare 之后、redo commit 之前。

  • 这是两阶段提交的关键顺序。

Q33:undo log 是回滚用的,为什么提交后还保留?

  • 供 MVCC 快照读找旧版本。

  • 没有 ReadView 需要时才由 purge 清理。

Q34:redo log 能关闭吗?

  • 不能。InnoDB 持久性依赖它。

  • 只能调刷盘策略,不能完全关。

Q35:binlog 能代替 redo log 做崩溃恢复吗?

  • 不能。binlog 是逻辑日志,重放慢且不精确。

  • 崩溃恢复必须用 redo 物理重放。

评论