📜 日志体系
redo log · binlog · undo log · 两阶段提交 · 主从复制原理
1. redo log、binlog、undo log 的区别?(必考)
| 对比 | redo log(重做) | binlog(归档) | undo log(回滚) |
|---|---|---|---|
| 层级 | InnoDB 存储引擎层 | MySQL Server 层 | InnoDB 存储引擎层 |
| 内容 | 物理日志:页级别的修改 | 逻辑日志:SQL 语句/行变更 | 逻辑:反向操作(旧值) |
| 作用 | 崩溃恢复(持久性) | 主从复制、数据恢复(备份/时间点恢复) | 事务回滚(原子性)+ MVCC 版本链 |
| 写入 | 事务执行中持续写,环形复用 | 事务提交时写 | 事务执行中持续写 |
为什么需要 redo log(WAL 机制):写磁盘太慢,每次修改先写内存缓冲池(Buffer Pool),把"页的变更"顺序写入 redo log(顺序 IO 快)。提交时 redo 落盘即可,脏页异步刷盘。崩溃后从 redo 重放恢复。WAL = Write Ahead Log,先写日志再改数据。
🎯 面试要点
- redo 是物理的("页 5 偏移 100 改成 xxx"),binlog 是逻辑的("UPDATE ... WHERE")——两者格式不同
- redo 保证不丢(持久性),binlog 保证能恢复和复制
- 刷盘策略:innodb_flush_log_at_trx_commit=1(每次提交刷盘,安全)/0(每秒刷,可能丢 1 秒)
2. 为什么 binlog 和 redo log 要两阶段提交?
redo log 在引擎层、binlog 在 Server 层,两个日志独立写。若不协调,崩溃时可能:redo 已提交但 binlog 没写(从库丢数据),或 binlog 写了但 redo 没提交(主从不一致)。
两阶段提交(prepare → commit):
- 事务执行:写 undo(回滚用)→ 改 Buffer Pool → 写 redo(prepare 状态)
- 提交时:先写 binlog(落盘)
- 再写 redo 的 commit 状态(此时才算真提交)
崩溃恢复规则:redo 是 prepare 且 binlog 完整 → 提交;redo prepare 但 binlog 不完整 → 回滚。保证 redo 与 binlog 状态一致。
🎯 面试要点
- binlog 是判断依据:binlog 完整才提交(因为从库靠 binlog 同步)
- MySQL 8.0 的两阶段提交没有被取消(仍是 redo prepare → 写 binlog → redo commit);8.0 的改进是组提交与 binlog 事务依赖跟踪(writeset),以及 redo 日志文件重构
- 阿里 canal 等中间件监听 binlog 做数据同步/增量,依赖的就是 binlog
3. MySQL 主从复制的原理?
- 主库 binlog 写入:事务提交时写 binlog
- 从库 IO 线程:连主库,请求 binlog,写入从库的 relay log(中继日志)
- 从库 SQL 线程:读取 relay log 并重放执行,数据同步完成
三种复制格式:
- STATEMENT:记录 SQL 语句。问题:非确定性函数(NOW()/UUID())主从不一致
- ROW(MySQL 5.7 默认):记录行变更,精确;日志量大
- MIXED:混合,默认 STATEMENT,不安全的语句用 ROW
复制延迟:主库写快、从库串行重放慢 → 延迟。常见:大事务、从库承担读压力、DDL。延迟会导致读写分离下的"刚写完读不到"。
🎯 面试要点
- 从库线程演进:MySQL 5.6 起支持并行复制(多线程 SQL 线程,按库/按事务)
- 主从延迟的缓解:缩小事务、读写分离路由强一致性走主库、半同步复制(等一个从库 ack)
- binlog 格式 + 隔离级别的关系:STATEMENT 下 RR 更安全(历史原因,MySQL 默认 RR 的由来)