Skip to content

MySQL redo log 与 binlog

redo log 和 binlog 都记录数据变化,但它们不是一回事。

一句话先记住:

redo log 解决“事务提交后,MySQL 宕机了,InnoDB 怎么把已提交的数据恢复回来”;binlog 解决“Server 层怎么记录这次变更,供主从复制、全量备份后的增量恢复、审计回放使用”。

如果只背“redo 是物理日志,binlog 是逻辑日志”,面试很容易卡住。真正要理解的是:MySQL 修改一行数据时,不会每次都把磁盘数据页立即刷回磁盘,而是先改内存 Buffer Pool,再写日志;redo log 保证 InnoDB 可以崩溃恢复,binlog 保证 MySQL Server 层可以把变更传出去和回放。

学习目标

目标你需要掌握什么
知道是什么redo log、binlog 分别记录什么、属于哪一层
知道为什么为什么不能只靠刷数据页保证持久性,为什么还需要 binlog
知道怎么工作Buffer Pool、WAL、redo prepare、写 binlog、redo commit、崩溃恢复
知道不这样会怎样redo/binlog 不一致会导致主库恢复结果和从库回放结果不一致
会项目落地能解释主从复制、备份恢复、大事务、刷盘参数、复制延迟
会面试能回答两阶段提交、组提交、sync_binloginnodb_flush_log_at_trx_commit

先理解一次 update 做了什么

假设执行:

sql
update account
set balance = balance - 100
where id = 1;

InnoDB 不是直接找到磁盘文件,把那一页立刻改掉并刷盘。更真实的过程是:

mermaid
flowchart TD
    A["客户端执行 update"] --> B["Server 层解析和优化 SQL"]
    B --> C["InnoDB 找到数据页"]
    C --> D{"数据页是否在 Buffer Pool"}
    D -- "不在" --> E["从磁盘读取页到 Buffer Pool"]
    D -- "已在" --> F["直接使用内存页"]
    E --> G["修改 Buffer Pool 中的数据页"]
    F --> G
    G --> H["生成 undo log"]
    G --> I["生成 redo log"]
    G --> J["Server 层生成 binlog event"]
    I --> K["提交阶段协调 redo 和 binlog"]
    J --> K
    K --> L["返回提交成功"]

这里要抓住三个关键词:

关键词含义
Buffer PoolInnoDB 的内存缓存,数据页先在内存里修改
WALWrite-Ahead Logging,先写日志,再择机刷数据页
脏页内存中的数据页已经修改,但还没刷到磁盘数据文件

为什么要这样设计?因为随机刷数据页很慢。数据页可能分散在磁盘文件不同位置,如果每次提交都强制刷数据页,写性能会很差。redo log 是顺序写,先把“这个页被怎么改了”记录下来,事务提交就可以先返回;后面再由刷脏页机制慢慢把 Buffer Pool 的脏页写回磁盘。

redo log 是什么

redo log 是 InnoDB 引擎层的重做日志,用于崩溃恢复。

它记录的是 InnoDB 数据页级别的修改信息,可以理解为:

某个表空间的某个数据页,在某个偏移位置,被改成了什么样。

不要把 redo log 理解成完整 SQL。redo log 面向的是 InnoDB 存储引擎,它关心的是数据页怎么恢复,而不是用户执行了哪条 SQL。

redo log 为什么能保证持久性

事务提交时,只要 redo log 按可靠策略写入磁盘,即使修改后的数据页还没有刷盘,MySQL 宕机后也可以通过 redo log 把数据页重新修好。

mermaid
flowchart TD
    A["事务修改 Buffer Pool 数据页"] --> B["数据页变成脏页"]
    B --> C["写 redo log"]
    C --> D{"redo log 是否已持久化"}
    D -- "是" --> E["事务可以认为提交可靠"]
    D -- "否" --> F["宕机可能丢失最近事务"]
    E --> G["后台线程择机刷脏页"]
    E --> H["宕机后用 redo log 重放恢复"]

这就是 WAL 的核心:先把恢复所需的日志写安全,再慢慢刷真正的数据页。

如果没有 redo log,就会出现很麻烦的问题:

  1. 事务提交时必须强制刷大量随机数据页,性能很差。
  2. 如果只改了内存页,宕机后磁盘数据文件还是旧的,提交过的数据会丢。
  3. 数据页可能只刷了一半,恢复时不知道哪些页应该恢复到什么状态。

redo log 的几个关键概念

概念解释
redo log buffer内存中的 redo 日志缓冲区
redo log file磁盘上的 redo 日志文件
LSNLog Sequence Number,日志序列号,用来标识日志推进位置
checkpoint检查点,表示哪些 redo 对应的脏页已经刷到磁盘
write把 redo 从用户态/内存写到操作系统缓存
fsync强制把日志落到磁盘或持久化设备

redo log 文件是循环写的。旧日志对应的脏页已经刷盘后,这部分日志空间就可以复用;如果脏页迟迟刷不下去,redo 空间被追上,MySQL 就必须加快刷脏页,写入性能会抖动。

mermaid
flowchart TD
    A["redo log 文件组"] --> B["顺序写入新日志"]
    B --> C["checkpoint 之前的日志可复用"]
    B --> D["checkpoint 之后的日志仍可能用于恢复"]
    D --> E{"写入点快追上 checkpoint"}
    E -- "是" --> F["加快刷脏页"]
    E -- "否" --> G["继续正常写入"]

binlog 是什么

binlog 是 MySQL Server 层的二进制日志。它记录的是数据库层面的逻辑变更事件,主要用于:

  1. 主从复制。
  2. 全量备份之后的增量恢复。
  3. 数据误操作后的时间点恢复。
  4. 数据变更审计和同步到下游系统。

binlog 不属于 InnoDB,而属于 MySQL Server 层。因此即使使用其他存储引擎,也可以有 binlog。

binlog 的三种格式

格式记录内容优点风险
STATEMENT原始 SQL 语句日志量小,可读性强非确定性函数、触发器、执行计划差异可能导致主从不一致
ROW每行修改前后数据主从一致性最好,生产最常用日志量较大
MIXEDSQL 和行模式混合尝试兼顾体积和一致性判断规则增加理解成本

商业项目通常优先使用 ROW,尤其是订单、支付、库存、资产、采集任务这类不能接受主从不一致的系统。

查看格式:

sql
show variables like 'binlog_format';

redo log 与 binlog 对比

项目redo logbinlog
所属层InnoDB 引擎层MySQL Server 层
核心目标崩溃恢复,保证提交事务不丢复制、备份恢复、审计回放
记录粒度数据页物理修改SQL 事件或行变更事件
写入方式循环写,空间可复用追加写,一个文件写满切下一个
是否所有引擎都有不是,主要是 InnoDB是 Server 层能力
是否用于主从复制不直接用于复制主从复制核心依据
是否用于崩溃恢复不是 InnoDB 崩溃恢复主力
提交一致性通过事务提交和两阶段提交通过两阶段提交与 redo 对齐

简单记法:

你关心的问题看哪个日志
宕机后提交事务会不会丢redo log
从库为什么能同步主库变更binlog
误删数据后如何恢复到某个时间点全量备份 + binlog
为什么长事务导致复制延迟binlog 大事务提交后从库回放慢
为什么写入突然抖动redo 空间、刷脏页、磁盘 fsync

为什么不能只要 redo log

redo log 可以让 InnoDB 自己恢复,但它不适合做主从复制和跨系统回放。

原因包括:

  1. redo log 是 InnoDB 私有格式,和存储引擎强绑定。
  2. redo log 是循环写,旧日志会被覆盖,不适合作长期增量恢复。
  3. redo log 记录的是页级变化,不适合给从库或下游系统理解业务变更。
  4. MySQL Server 层需要一种统一日志来支持复制和恢复,这就是 binlog。

为什么不能只要 binlog

binlog 不能替代 InnoDB 的崩溃恢复。

原因包括:

  1. binlog 是 Server 层逻辑日志,不知道 InnoDB 数据页刷盘到了什么程度。
  2. 宕机恢复时,InnoDB 需要根据页 LSN、redo LSN 判断哪些页要重放。
  3. binlog 追加写,不负责 Buffer Pool 脏页恢复。
  4. 如果每次提交都靠刷数据页保证持久化,性能会很差。

所以两个日志都需要:redo 管引擎内部可靠性,binlog 管复制和外部恢复。

两阶段提交是什么

更新事务提交时,MySQL 要保证 redo log 和 binlog 在事务边界上一致。典型流程是:

mermaid
sequenceDiagram
    participant C as "Client"
    participant S as "Server 层"
    participant I as "InnoDB"
    C->>S: "commit"
    S->>I: "写 redo log prepare"
    I-->>S: "prepare 完成"
    S->>S: "写 binlog"
    S->>I: "写 redo log commit"
    S-->>C: "提交成功"

这个过程叫两阶段提交。这里的两阶段不是分布式事务里的 XA 跨库两阶段提交,而是 MySQL Server 层协调 binlog 和 InnoDB redo log 的内部提交过程。

为什么需要两阶段提交

如果没有两阶段提交,redo log 和 binlog 可能只成功一个。

场景一:先写 redo,再写 binlog

mermaid
flowchart TD
    A["redo log 写成功"] --> B["MySQL 宕机"]
    B --> C["binlog 没写成功"]
    C --> D["主库崩溃恢复后有这次更新"]
    C --> E["从库没有收到这次更新"]
    D --> F["主从数据不一致"]
    E --> F

主库靠 redo 恢复出这次更新,但 binlog 没有记录,从库无法复制这次更新。

场景二:先写 binlog,再写 redo

mermaid
flowchart TD
    A["binlog 写成功"] --> B["MySQL 宕机"]
    B --> C["redo log 没写成功"]
    C --> D["主库崩溃恢复后没有这次更新"]
    C --> E["从库可能根据 binlog 回放了这次更新"]
    D --> F["主从数据不一致"]
    E --> F

从库按 binlog 回放了更新,但主库恢复后没有这次更新,也会不一致。

两阶段提交的目的就是让 MySQL 崩溃恢复时能判断:

崩溃位置恢复判断
redo prepare 前崩溃事务没有准备成功,回滚
redo prepare 后、binlog 前崩溃binlog 不存在,回滚
binlog 写完、redo commit 前崩溃binlog 存在,提交
redo commit 后崩溃事务已提交

更口语化地说:redo 先进入 prepare 状态,等 binlog 写成功后,再把 redo 标记为 commit。崩溃恢复时,如果发现 redo 是 prepare,就去检查对应 binlog 是否完整;binlog 完整则提交,否则回滚。

一次事务提交的完整链路

mermaid
flowchart TD
    A["执行 update"] --> B["生成 undo log"]
    B --> C["修改 Buffer Pool 数据页"]
    C --> D["生成 redo log 到 redo buffer"]
    D --> E["提交阶段"]
    E --> F["redo log 写入 prepare"]
    F --> G["binlog 写入文件"]
    G --> H["redo log 写入 commit"]
    H --> I["返回客户端成功"]
    I --> J["后台刷脏页到数据文件"]

这里 undo log 也很重要:它支持事务回滚和 MVCC 旧版本。undo log 本身的修改也需要 redo log 保护,否则宕机后连“怎么回滚、怎么找旧版本”都可能不可靠。MVCC 与 undo log 的关系见:MVCC

崩溃恢复时怎么判断

MySQL 重启后,InnoDB 会扫描 redo log:

mermaid
flowchart TD
    A["MySQL 重启"] --> B["扫描 redo log"]
    B --> C{"事务 redo 状态"}
    C -- "commit" --> D["重放 redo,保证数据页恢复"]
    C -- "prepare" --> E["检查 binlog 是否完整"]
    E -- "完整" --> D
    E -- "不完整" --> F["回滚事务"]
    C -- "未完成" --> F

这就是为什么 redo 和 binlog 要一起理解。redo 决定 InnoDB 能不能恢复数据页,binlog 决定 prepare 状态的事务是否已经完成 Server 层提交。

刷盘参数怎么影响可靠性

innodb_flush_log_at_trx_commit

这个参数控制 redo log 的刷盘策略。

行为可靠性性能
0每秒写入并刷盘 redo宕机可能丢 1 秒事务最好
1每次事务提交都写入并刷盘 redo最可靠成本最高
2每次提交写入 OS cache,每秒刷盘操作系统崩溃可能丢 1 秒折中

生产核心交易系统通常用 1。如果是日志采集、临时统计、可重放数据,可以结合业务容忍度评估。

sync_binlog

这个参数控制 binlog 的刷盘策略。

行为风险
0由操作系统决定何时刷盘机器宕机可能丢 binlog
1每次事务提交都刷 binlog最可靠,性能成本高
N每 N 次提交刷一次最多可能丢 N 个事务左右的 binlog

核心系统常见组合:

sql
set persist innodb_flush_log_at_trx_commit = 1;
set persist sync_binlog = 1;

这组配置可靠性高,但写入吞吐会受磁盘 fsync 能力影响。高并发写入场景会结合 SSD、组提交、事务拆分、批量写入来优化。

组提交是什么

如果每个事务都单独 fsync 一次 redo 和 binlog,磁盘压力会很大。MySQL 会尽量把一批同时提交的事务组合起来,统一写日志和刷盘,这叫组提交。

mermaid
flowchart TD
    A["多个事务几乎同时 commit"] --> B["进入提交队列"]
    B --> C["合并写 binlog"]
    C --> D["一次或少量 fsync"]
    D --> E["批量完成提交"]

组提交的意义是:在保证事务顺序和一致性的前提下,减少 fsync 次数,提高吞吐。

但大事务会破坏这个效果。一个事务里更新几十万行,binlog 和 redo 都很大,从库回放也慢,线上常见现象就是提交抖动、复制延迟、备份恢复时间变长。

和 undo log 的区别

redo、undo、binlog 经常一起出现,但职责完全不同:

日志主要作用典型问题
redo log崩溃后重做已提交修改提交后宕机为什么不丢
undo log回滚事务、提供 MVCC 旧版本回滚怎么恢复,快照读怎么看旧值
binlog复制、备份恢复、审计从库怎么同步,误删怎么恢复

执行 update 时通常会同时涉及它们:

  1. undo log 记录修改前的旧值,回滚和 MVCC 要用。
  2. redo log 记录页修改,宕机恢复要用。
  3. binlog 记录 Server 层变更事件,复制和恢复要用。

更细一点:undo log 存在于 InnoDB 的 undo 表空间或回滚段中,它本身也是数据库页。对 undo 页的修改也会产生 redo log,所以 redo 保护的不只是业务数据页,也包括 undo 页等 InnoDB 内部页。

商业常用场景

主从复制

主库提交事务后写 binlog,从库 IO 线程拉取 binlog,SQL 线程或多线程复制回放 relay log。

mermaid
flowchart TD
    A["主库提交事务"] --> B["写 binlog"]
    B --> C["从库 IO 线程拉取"]
    C --> D["写 relay log"]
    D --> E["从库 SQL 线程回放"]
    E --> F["从库数据追上主库"]

复制延迟常见原因:

原因解释
主库大事务从库必须回放大量行变更
从库慢 SQL回放速度低于主库写入速度
从库硬件弱CPU、IO、磁盘能力不足
锁等待从库回放线程被查询或事务阻塞
单表热点并行复制难以充分拆分

误删数据恢复

常见恢复思路:

  1. 找到最近一次全量备份。
  2. 恢复到临时库。
  3. 用 binlog 回放到误操作前的时间点。
  4. 校验数据后,把缺失数据补回生产。

redo log 不能替代这个过程,因为 redo 是循环写且面向 InnoDB 内部恢复,不适合做长期时间点恢复。

订单和支付系统

订单、支付、库存这类系统通常要求:

  1. binlog_format=ROW,减少主从不一致风险。
  2. sync_binlog=1,降低 binlog 丢失风险。
  3. innodb_flush_log_at_trx_commit=1,保证提交事务尽量不丢。
  4. 避免超大事务,降低提交抖动和复制延迟。
  5. 关键表变更需要审计或同步下游时,基于 binlog 做 CDC。

SQL Demo:查看日志配置

查看 binlog 是否开启:

sql
show variables like 'log_bin';
show variables like 'binlog_format';
show binary logs;
show master status;

MySQL 8.0.22 之后更推荐:

sql
show binary log status;

查看可靠性相关参数:

sql
show variables like 'sync_binlog';
show variables like 'innodb_flush_log_at_trx_commit';

查看 redo 相关指标:

sql
show engine innodb status\G

在输出中关注 LOG 段,例如 LSN、checkpoint、pending writes 等信息。不同 MySQL 版本字段会有差异,学习时重点看日志推进和刷盘压力。

SQL Demo:观察 binlog 事件

开启 binlog 的环境中,可以先查看当前文件:

sql
show binary log status;

执行一个小事务:

sql
create table if not exists log_demo_account (
  id bigint primary key,
  balance int not null
) engine = InnoDB;

insert into log_demo_account(id, balance)
values (1, 1000)
on duplicate key update balance = values(balance);

begin;
update log_demo_account
set balance = balance - 100
where id = 1;
commit;

再用命令行查看 binlog:

bash
mysqlbinlog --base64-output=decode-rows -vv mysql-bin.000001

如果是 ROW 格式,你会看到表行变更事件,而不是简单的一条原始 SQL。这能帮助你理解为什么 ROW 更适合保证复制一致性。

生产排查流程

主从延迟

mermaid
flowchart TD
    A["发现主从延迟"] --> B["查看主库是否有大事务"]
    B --> C["查看从库复制线程状态"]
    C --> D["检查从库锁等待和慢 SQL"]
    D --> E["检查磁盘 IO 和 CPU"]
    E --> F["评估拆分事务或开启并行复制"]

常用命令:

sql
show replica status\G;
show processlist;
select *
from information_schema.innodb_trx;

老版本 MySQL 使用:

sql
show slave status\G;

写入抖动

mermaid
flowchart TD
    A["写入 RT 突然升高"] --> B["检查磁盘 fsync 延迟"]
    B --> C["检查 redo checkpoint 压力"]
    C --> D["检查大事务和批量更新"]
    D --> E["检查 Buffer Pool 脏页比例"]
    E --> F["优化事务大小和 IO 能力"]

常见处理方向:

现象处理方向
大事务提交慢拆分批次,每批几百到几千行
从库延迟高减少大事务,优化从库 SQL,开启并行复制
fsync 慢检查磁盘、云盘规格、文件系统、IO 饱和
checkpoint 压力大调整 redo 容量、刷脏页能力、写入峰值

常见误区

误区正确理解
redo log 和 binlog 都是 SQL 日志redo 面向 InnoDB 页恢复,binlog 才面向复制和回放
有 binlog 就不需要 redobinlog 不能负责 Buffer Pool 脏页崩溃恢复
有 redo 就不需要 binlogredo 不适合主从复制和时间点恢复
提交成功等于数据页已经刷盘提交成功通常代表日志可靠,数据页可以后刷
sync_binlog=1 没成本每次提交刷盘可靠性高,但会增加 IO 成本
大事务只是执行慢大事务还会放大 redo、binlog、锁等待、复制延迟和恢复成本

面试标准回答

redo log 和 binlog 有什么区别?

redo log 是 InnoDB 引擎层日志,主要用于崩溃恢复,记录数据页的物理修改,采用循环写。事务提交后,即使数据页还没刷盘,只要 redo log 可靠落盘,MySQL 重启后就能重放 redo 恢复已提交数据。binlog 是 MySQL Server 层日志,主要用于主从复制、备份后的增量恢复和审计,记录 SQL 事件或行变更事件,采用追加写。两者通过两阶段提交保证同一事务在 redo 和 binlog 中保持一致。

为什么需要两阶段提交?

因为 redo log 和 binlog 属于不同层。如果 redo 写成功但 binlog 没写成功,主库崩溃恢复后有这次变更,从库却无法复制;如果 binlog 写成功但 redo 没写成功,从库可能回放了变更,主库恢复后却没有。两阶段提交先让 redo 进入 prepare 状态,再写 binlog,最后提交 redo。崩溃恢复时,如果 redo 是 prepare,就检查 binlog 是否完整,完整则提交,不完整则回滚。

sync_binloginnodb_flush_log_at_trx_commit 怎么设置?

核心交易系统通常设置 sync_binlog=1innodb_flush_log_at_trx_commit=1,可靠性最高,表示每次事务提交都尽量把 binlog 和 redo 刷盘。代价是 fsync 成本更高,写入吞吐依赖磁盘能力。对可重放、可容忍少量丢失的数据,可以根据业务容忍度选择更偏性能的配置,但订单、支付、资产、库存类系统不建议随意降低可靠性。

小结

  1. redo log 属于 InnoDB,解决崩溃恢复和持久性。
  2. binlog 属于 Server 层,解决主从复制、增量恢复和审计。
  3. MySQL 通过两阶段提交让 redo 和 binlog 在事务边界保持一致。
  4. 提交成功通常不代表数据页已经刷盘,而是日志已经按策略可靠写入。
  5. undo log 负责回滚和 MVCC 旧版本,undo 页的修改也会受到 redo 保护。
  6. 生产中要重点关注大事务、复制延迟、刷盘参数、磁盘 fsync 和恢复链路。

关联学习:

  • 事务:理解 ACID 和事务提交。
  • MVCC:理解 undo log 如何支撑快照读。
  • :理解当前读、写写冲突和间隙锁。