一、引言:日志是数据库可靠性的基石
在现代数据库系统中,日志(Logging) 是实现 原子性(Atomicity) 和 持久性(Durability) 的核心机制。根据记录内容的抽象层次,数据库日志通常分为三类:
- 逻辑日志(Logical Logging)
- 物理日志(Physical Logging)
- 物理逻辑日志(Physiological Logging)
这三种日志在粒度、语义、恢复方式和适用场景上存在显著差异。
二、三种日志格式详解
1. 逻辑日志(Logical Logging)
- 定义:记录事务的高层操作语义,如 SQL 语句或行级变更。
- 特点:
- 与存储结构无关;
- 可跨版本、跨引擎重放;
- 适用于复制、逻辑备份。
- MySQL 实现:Binary Log(binlog)
- STATEMENT 格式:记录原始 SQL;
- ROW 格式:记录主键 + 列值变更(仍属逻辑层,因不涉及页/偏移)。
- mixed格式:混合模式
示例(ROW 格式):
Table_map: test.T
Write_rows: (c1=1, c2='abc')
2. 物理日志(Physical Logging)
- 定义:记录底层存储的字节级变化,如“将页 X 偏移 Y 处的 Z 字节从 A 改为 B”。
- 特点:
- 高度依赖页格式;
- 恢复时直接 memcpy 覆盖内存;
- 无法支持页结构演进。
- MySQL 实现:InnoDB 未使用纯物理日志。
- 典型系统:LevelDB WAL、早期 Berkeley DB。
InnoDB 不采用此模式,因其无法兼容压缩页、加密页等高级特性。
3. 物理逻辑日志(Physiological Logging)
- 定义:以 物理页为作用域,记录 页内的逻辑操作,在页 X 上插入记录 Y。
- 特点:
- 日志 = <page_id, operation, args>;
- 恢复时调用存储引擎函数重做操作(非字节覆写);
- 兼顾效率与灵活性。
- MySQL 实现:InnoDB Redo Log
✅ 示例:
<MLOG_REC_INSERT, space=1, page_no=100, record=(1,'abc'), index_id=PRIMARY>
<MLOG_REC_INSERT, space=1, page_no=200, record=(1,ptr), index_id=key_c1>
三、以一条 INSERT 为例:三种日志的生成对比
假设执行:
CREATE TABLE T (c1 INT, c2 VARCHAR(10), KEY(c1)) ENGINE=InnoDB;
INSERT INTO T VALUES (1, 'abc');
插入一行 (1, 'abc'),涉及:
- 聚簇索引(主键索引,默认基于 rowid 或隐式主键)
- 二级索引 key_c1
通常至少修改 两个 B+ 树页(聚簇索引页 + 二级索引页)。
1. 逻辑日志(Logical Log)记录情况
- 记录内容:事务的高层语义
- 示例:<INSERT, table=T, values=(1, 'abc')>
- MySQL 对应:binlog(ROW 格式)Table_map: T
Write_rows: (1, 'abc') - 数量:1 条(整个 INSERT 操作)
2. 物理逻辑日志(Physiological Log)记录情况
- 记录内容:页 ID + 页内逻辑操作
- 示例:<MLOG_REC_INSERT, space_id=1, page_no=100, record=(1, 'abc', DB_ROW_ID=…), index_id=PRIMARY>
<MLOG_REC_INSERT, space_id=1, page_no=200, record=(1, ptr_to_clustered), index_id=key_c1> - 特点:
- 每条日志绑定一个 page_no;
- 记录的是 “在页 X 上插入记录 Y”,而非字节;
- 恢复时需调用 InnoDB 的 page_cur_insert_rec_low() 等函数重做。
- MySQL 对应:InnoDB Redo Log
- 数量:≥2 条(每个被修改的页至少一条)
特殊情况
只有聚簇索引(无二级索引):插入数据
InnoDB 内部操作:
- 创建新页的日志(MLOG_PAGE_CREATE);
- 更新父页指针的日志(如果是非叶子页)。
| 插入到已有页(无分裂) | 1 条(MLOG_REC_INSERT) |
| 插入导致页分裂 | ≥2 条(MLOG_PAGE_CREATE + MLOG_REC_INSERT + 可能的父页更新) |
即使只有 1 条用户记录插入,也可能因 B+ 树结构维护产生多条 redo log。
3. 物理日志(Physical Log)记录情况
- 记录内容:字节级变更(offset + old/new value)
- 示例(针对一个页的多次修改):<space=1, page=100, offset=24, new_value=0x0005> // 页头:记录数从 4→5
<space=1, page=100, offset=16380, new_value=0x0064> // Slot 数组更新
<space=1, page=100, offset=200, new_value=…> // 新记录内容
<space=1, page=100, offset=prev_rec+2, new_value=…> // 前一条记录的 next 指针 - 数量:每个页可能产生 N 条(N ≥ 3~5 很常见)
- MySQL 是否使用?:否。InnoDB 不使用纯物理日志。
对比表
| 逻辑日志 | 表/行级操作 | <INSERT, T, (1,'abc')> | 1 条 | 通过 binlog |
| 物理逻辑日志 | 页 + 页内逻辑操作 | 两条页级插入操作,分别对应聚簇索引和二级索引页的插入操作 | ≥2 条 | InnoDB redo log |
| 物理日志 | 字节偏移 | 多条字节修改(页头、slot、指针等) | ≥2×N 条 | InnoDB 不生成 |
关键洞察:
InnoDB 跳过纯物理日志,直接从逻辑操作生成 physiological redo log,避免了字节日志的冗余与脆弱性。
四、InnoDB 为何选择 Physiological Logging?
| 支持页格式演进 | 即使 InnoDB 修改页结构(如引入新字段),只要操作语义不变,旧日志仍可重放 |
| 兼容压缩/加密页 | 日志记录“插入记录”,而非“写入偏移”,可在解压后页上安全重做 |
| 日志体积小 | 插入一条记录仅需 ~50 字节日志,而非 16KB 页 |
| 恢复安全性高 | 通过调用 B+ 树函数重做,确保页结构一致性(如链表、slot 更新正确) |
五、日志协同:Redo Log + Binlog + Doublewrite Buffer
InnoDB 的可靠性依赖三大组件协同:

- Redo Log(Physiological):保证页级操作可重做;
- Doublewrite Buffer:保证页本身完整(防 partial page write);
- Binlog(Logical):保证事务可复制、可 PITR(时间点恢复)。
注意:只有当 innodb_flush_log_at_trx_commit=1 且 sync_binlog=1 时,才能实现真正的 crash-safe replication。
六、三种日志核心特性
| 记录粒度 | 表/行级 | 字节级 | 页 + 页内操作 |
| 是否依赖存储结构 | 否 | 是 | 部分(需页 ID) |
| 恢复方式 | 重执行 SQL | memcpy 覆写 | 调用存储引擎函数 |
| 日志体积 | 小 | 极大 | 中等 |
| MySQL 组件 | binlog | 无 | InnoDB redo log |
| 适用场景 | 复制、备份 | 简单 KV 存储 | 崩溃恢复 |
MySQL 通过 分层日志设计 实现了高可靠与高性能的统一:
- 逻辑日志(binlog) 面向应用与复制;
- 物理逻辑日志(redo log) 面向存储引擎崩溃恢复;
- Doublewrite Buffer 作为最后一道防线,保障页完整性。
七、面试题
Q:MySQL 中哪些日志属于逻辑日志?哪些属于物理逻辑日志?
A:binlog 是逻辑日志;InnoDB redo log 是物理逻辑日志(physiological logging)。
Q:为什么 InnoDB 不使用纯物理日志?
A:纯物理日志无法支持页压缩、加密、格式演进,且恢复时缺乏语义校验,易导致页损坏。
Q:一条 INSERT 会产生几条 redo log?为什么?
A:至少两条——聚簇索引页和每个二级索引页各一条,因为每个 B+ 树页的修改需独立记录。
Q:Physiological logging 如何保证恢复正确性?
A:恢复时调用 InnoDB 的页管理函数(如 insert/delete)重做操作,确保页内结构(链表、slot 等)一致。
Q:binlog 和 redo log 能否互相替代?
A:不能。binlog 用于实例级复制,redo log 用于引擎级崩溃恢复,二者目标不同,必须共存。
网硕互联帮助中心








评论前必须登录!
注册