binlog、redo log 和 undo log 的区别?
结论:三者属于不同层、解决不同问题。redo log 是 InnoDB 的物理日志,保证「已提交的修改不丢」(崩溃恢复前滚);undo log 是 InnoDB 的逻辑旧版本,保证「未提交的修改能回滚」并支撑 MVCC 快照读;binlog 是 Server 层的逻辑日志,保证「主从复制与按时间点恢复」。redo 循环写、undo 随事务生命周期增长、binlog 追加写。
一、一张表分清
| 维度 | redo log | undo log | binlog |
|---|---|---|---|
| 所属层 | InnoDB 存储引擎 | InnoDB 存储引擎 | MySQL Server 层 |
| 日志类型 | 物理日志(页 + 偏移的变更) | 逻辑日志(反向操作/旧版本) | 逻辑日志(SQL 或行变更) |
| 写入时机 | 事务执行中持续写 | 修改前先写旧值 | 事务提交前写入 binlog cache |
| 落盘方式 | 循环写,固定大小 | 回滚段,按需扩展 | 追加写,按文件滚动 |
| 主要用途 | 崩溃恢复(前滚) | 回滚、MVCC | 主从复制、PITR 恢复 |
| 是否可关闭 | 不可 | 不可 | 可(skip_log_bin,但生产禁用) |
| 关键参数 | innodb_redo_log_capacity(8.0.30+) | innodb_undo_tablespaces | sync_binlog、binlog_format |
| 典型文件 | #innodb_redo/ 下若干文件 | undo_001、undo_002 | binlog.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;
四、三个常见误解
- 「undo 只用于回滚」:不对。MVCC 的快照读要靠 undo 版本链,长事务不提交会让 undo 无法 purge,历史版本越堆越多,查询越来越慢。
- 「redo log 越大越好」:太大导致崩溃恢复时间长、checkpoint 间隔拉长;太小则频繁 checkpoint、写性能抖动。
- 「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 管「能复制、能恢复到某一刻」。三者分属不同层、写入时机不同、生命周期不同,回答时把「层次 + 内容 + 用途 + 参数」四点讲清即可。