binlog、redo log 和 undo log 的区别?

#中间件#面试八股

binlog、redo log 和 undo log 的区别?

结论:三者属于不同层、解决不同问题。redo log 是 InnoDB 的物理日志,保证「已提交的修改不丢」(崩溃恢复前滚);undo log 是 InnoDB 的逻辑旧版本,保证「未提交的修改能回滚」并支撑 MVCC 快照读;binlog 是 Server 层的逻辑日志,保证「主从复制与按时间点恢复」。redo 循环写、undo 随事务生命周期增长、binlog 追加写。

一、一张表分清

维度redo logundo logbinlog
所属层InnoDB 存储引擎InnoDB 存储引擎MySQL Server 层
日志类型物理日志(页 + 偏移的变更)逻辑日志(反向操作/旧版本)逻辑日志(SQL 或行变更)
写入时机事务执行中持续写修改前先写旧值事务提交前写入 binlog cache
落盘方式循环写,固定大小回滚段,按需扩展追加写,按文件滚动
主要用途崩溃恢复(前滚)回滚、MVCC主从复制、PITR 恢复
是否可关闭不可不可可(skip_log_bin,但生产禁用)
关键参数innodb_redo_log_capacity(8.0.30+)innodb_undo_tablespacessync_binlog、binlog_format
典型文件#innodb_redo/ 下若干文件undo_001、undo_002binlog.000001

二、一条 UPDATE 的写入时序

UPDATE account SET balance = 90 WHERE id = 1;   -- 原值 100

 ① 从 buffer pool 取页(未命中则从磁盘读)
 ② 记录 undo:把 balance 改回 100 的反向操作
 ③ 修改 buffer pool 中的页,标记为脏页
 ④ 写 redo log buffer(页的物理变更)
 ⑤ 写 binlog cache(row 格式:before/after 镜像)
 ⑥ COMMIT:
      redo 按 innodb_flush_log_at_trx_commit 落盘
      binlog 按 sync_binlog 落盘
      两阶段提交(prepare → commit)保证两者一致
 ⑦ 脏页由后台线程异步刷盘(checkpoint)

关键认知:redo 不是 binlog 的缓存。redo 记录的是「哪个页的哪个字节改成了什么」,用于崩溃后重放;binlog 记录的是「逻辑上发生了什么」,用于复制与恢复。二者内容、格式、生命周期都不同。

三、参数与实践

[mysqld]
# redo:8.0.30 起用总容量控制,替代 innodb_log_file_size
innodb_redo_log_capacity        = 4G
innodb_flush_log_at_trx_commit  = 1     # 每次提交都刷盘,最安全

# binlog
log_bin                = /var/log/mysql/binlog
binlog_format          = ROW             # 推荐,复制一致性好
binlog_row_image       = FULL            # 记录前后镜像,便于闪回
sync_binlog            = 1               # 每次提交刷盘
expire_logs_days       = 7               # 或 binlog_expire_logs_seconds
max_binlog_size        = 512M

# undo
innodb_undo_tablespaces = 2
innodb_max_undo_log_size = 1G

innodb_flush_log_at_trx_commit=1 与 sync_binlog=1 是「双 1 配置」,代表每次提交都要落盘,性能最差但数据最安全;对一致性要求低的日志类业务可以放宽到 2 或 0。

-- 看 binlog 列表与当前写入位置
SHOW BINARY LOGS;
SHOW MASTER STATUS;          -- 8.0.22+ 也可用 SHOW REPLICA STATUS 的对应项

-- 解析 binlog,按时间点恢复时用来定位起点
SHOW BINLOG EVENTS IN 'binlog.000007' LIMIT 20;

-- 看 redo 的 LSN 与 checkpoint 推进情况
SHOW ENGINE INNODB STATUS\G
-- LOG 段:Log sequence number / Last checkpoint at

-- 长事务会拖住 undo 回收与 redo checkpoint
SELECT trx_id, trx_started, TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS secs
FROM information_schema.innodb_trx
ORDER BY trx_started
LIMIT 10;

四、三个常见误解

  1. 「undo 只用于回滚」:不对。MVCC 的快照读要靠 undo 版本链,长事务不提交会让 undo 无法 purge,历史版本越堆越多,查询越来越慢。
  2. 「redo log 越大越好」:太大导致崩溃恢复时间长、checkpoint 间隔拉长;太小则频繁 checkpoint、写性能抖动。
  3. 「binlog 只能做复制」:它同时是按时间点恢复(PITR)与数据订阅(Canal)的基础,binlog_row_image=FULL 还能支持闪回工具。

五、容量与故障视角

故障场景依赖哪个日志结果
实例断电redo已提交事务前滚,未提交回滚
误删数据binlog用备份 + binlog 回放到误删前一刻
主从数据不一致binlog重新搭建从库或跳过错误事务
长事务拖垮实例undo历史版本堆积,磁盘与查询性能双降
磁盘写满redo/binlog 任一实例只读或直接崩溃

常见追问

  • 为什么需要两阶段提交? redo 与 binlog 是两个独立的持久化系统,若不协调,崩溃后可能出现「主库有、binlog 无」导致从库丢数据,或反之导致主从不一致。
  • redo log 能关掉吗? 不能,它是 InnoDB 崩溃恢复的基础。
  • binlog_format 怎么选? 生产用 ROW;STATEMENT 日志小但函数、随机值会导致主从不一致;MIXED 是折中。
  • undo 会一直增长吗? 不会,purge 线程会回收;但长事务会阻塞 purge,需要监控 History list length。
  • redo 和 buffer pool 的关系? 修改先落 buffer pool,redo 保证崩溃后能重放;脏页刷盘与 redo 写入是两条异步路径。
  • binlog 太大怎么办? 调 max_binlog_size、缩短保留期、必要时做一次全量备份后清理旧文件。

小结

记忆口诀:redo 管「不丢」,undo 管「能退」与「能读旧版本」,binlog 管「能复制、能恢复到某一刻」。三者分属不同层、写入时机不同、生命周期不同,回答时把「层次 + 内容 + 用途 + 参数」四点讲清即可。

笔记加载中…