这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 InnoDB 以表空间管理文件,以页为基本 IO 单位。理解页面布局是理解 B+Tree、Buffer Pool、redo 和恢复的基础。
13.1 文件类型
| 文件 | 职责 |
|---|---|
| system tablespace | 数据字典历史结构、undo、双写等,取决于配置 |
file-per-table .ibd |
每表独立表空间 |
| general tablespace | 多表共享 |
| temporary tablespace | 临时数据 |
| undo tablespace | 回滚段 |
| redo log | WAL 日志 |
SHOW VARIABLES LIKE 'innodb_file_per_table';
SELECT SPACE, NAME FROM information_schema.innodb_sys_tablespaces LIMIT 10;
13.2 空间组织
tablespace
-> segment
-> extent = 1MB
-> page = 16KB
默认:
| 单位 | 大小 |
|---|---|
| page | 16KB |
| extent | 64 pages |
| segment | 若干 extent |
页号是表空间内定位页的基本编号。
13.3 页类型
| 类型 | 用途 |
|---|---|
| FSP_HDR | 文件空间头 |
| IBUF_BITMAP | Change Buffer 位图 |
| INODE | 段信息 |
| INDEX | B+Tree 页 |
| SYSTEM | 系统页 |
| TRX_SYS | 事务系统 |
| UNDO_LOG | Undo 页 |
| BLOB | 大字段页 |
文件头包含页号、表空间 ID、校验和、LSN 等信息。
13.4 INDEX 页布局
fil header
page header
infimum + supremum
user records
free space
page directory
fil trailer
关键区域:
infimum:最小虚拟记录;supremum:最大虚拟记录;- user records:按主键顺序组织的记录;
- page directory:槽,支持二分定位;
- trailer:校验和与 LSN。
13.5 行溢出
InnoDB 尽量将行放在 B+Tree 页内。长 VARCHAR、TEXT、BLOB 可能溢出到 BLOB 页。
CREATE TABLE t(
id BIGINT PRIMARY KEY,
body TEXT
) ENGINE=InnoDB;
影响:
- 聚簇索引页存放前缀;
- 剩余数据放溢出页;
- 读取大字段增加 IO;
- 行格式影响前缀长度;
- 大字段会降低缓冲命中率。
查看行格式:
SELECT NAME, ROW_FORMAT
FROM information_schema.innodb_tables
WHERE NAME LIKE 'db/t';
13.6 Doublewrite
脏页
-> doublewrite file
-> 数据文件
作用:防止部分页写坏。若数据文件写入中断,可先从 doublewrite 恢复完整页,再应用 redo。
SHOW VARIABLES LIKE 'innodb_doublewrite';
13.7 表空间监控
SELECT file_name, tablespace_name, total_extents, free_extents
FROM information_schema.files;
SHOW GLOBAL STATUS LIKE 'Innodb_data%';
关注:
.ibd增长;- 碎片;
- 临时表空间膨胀;
- undo 表空间;
- redo 容量;
- 可用磁盘水位。
13.8 源码入口
| 文件 | 内容 |
|---|---|
fil0fil.cc |
文件和表空间 |
fut0lst.cc |
文件链表 |
fsp0fsp.cc |
文件空间管理 |
page0page.cc |
页面操作 |
buf0buf.cc |
页缓存 |
调试:
b fil_space_t::io
b buf_page_get_gen
本章小结
表空间由段、区和页组成,INDEX 页是 B+Tree 的物理载体。页头、页目录、记录区和校验信息共同支撑定位与恢复;行溢出和 doublewrite 会直接影响 IO 与可靠性。
思考题
- extent 和 page 的关系是什么?
- page directory 有什么作用?
- 为什么需要 doublewrite?
- 长字段为什么会降低缓存效率?
- 如何查看表空间和页面相关状态?