InnoDB存储层次

表空间(Tablespace)
  └─ 段(Segment)
      └─ 区(Extent,1MB = 64页)
          └─ 页(Page,16KB,InnoDB的基本IO单位)
              └─ 行(Row)

1. 表空间(Tablespace)

定义

表空间是InnoDB存储数据的逻辑单位,包含多个段。

分类

类型文件名内容配置参数
系统表空间ibdata1数据字典、undo log、双写缓冲innodb_data_file_path
独立表空间table_name.ibd表数据+索引innodb_file_per_table
通用表空间tablespace.ibd多个表共享CREATE TABLESPACE
临时表空间ibtmp1临时表、排序缓冲innodb_temp_data_file_path
Undo表空间undo_001undo_002undo log(MySQL 8.0+)innodb_undo_tablespaces

系统表空间 vs 独立表空间

-- 查看配置
SHOW VARIABLES LIKE 'innodb_file_per_table';
-- ON:每个表独立.ibd文件(推荐)
-- OFF:所有表共享ibdata1(不推荐)

-- 查看表空间文件
# ls -lh /var/lib/mysql/test/
-rw-r----- 1 mysql mysql  16M users.ibd  -- 独立表空间

优点

  • 独立表空间易于管理(可单独备份、迁移)
  • 碎片整理不影响其他表
  • DELETE后空间可回收

缺点

  • 文件数量多

2. 段(Segment)

定义

是一组区的集合,用于管理索引和数据。

分类

段类型作用存储内容
数据段存储表数据聚簇索引的叶子节点
索引段存储索引数据二级索引的非叶子节点
回滚段存储undo log事务回滚信息
B+树索引结构
       根节点(索引段)
      /      \
   分支节点    分支节点(索引段)
   /    \    /    \
 叶子节点 叶子节点 叶子节点 叶子节点(数据段)

3. 区(Extent)

定义

是连续的64个页(1MB),是InnoDB分配空间的单位。

1个区(Extent)= 64个页 = 64 × 16KB = 1MB

作用

提高磁盘IO效率,减少碎片。

-- 顺序扫描一个区
连续读取64个页(1MB),只需要1次磁盘寻址
-- vs 随机读取64个页
需要64次磁盘寻址(慢)

区的状态

状态说明
FREE空闲,未被使用
FREE_FRAG碎片区,部分页已使用
FULL_FRAG碎片区,所有页已使用
FSEG被段完全占用

4. 页(Page)

定义

是InnoDB的基本IO单位,默认大小16KB。

-- 查看页大小
SHOW VARIABLES LIKE 'innodb_page_size';
-- 16384(16KB,默认)

为什么是16KB?

  1. 磁盘IO优化:一次IO读取16KB,减少IO次数
  2. 缓存效率:Buffer Pool以页为单位缓存
  3. B+树高度:16KB可以存储更多索引项,减少树高度

页的类型

页类型作用
数据页存储表数据(叶子节点)
索引页存储索引数据(非叶子节点)
Undo页存储undo log
系统页存储系统信息(如数据字典)
BLOB页存储大对象(TEXT、BLOB)

页的内部结构

┌──────────────────────────────────────┐
│ File Header(38字节)                  │  页头,校验和、页号、前后页指针
├──────────────────────────────────────┤
│ Page Header(56字节)                 │  页状态信息
├──────────────────────────────────────┤
│ Infimum + Supremum(26字节)          │  虚拟最小/最大记录
├──────────────────────────────────────┤
│ User Records(动态)                   │  实际数据行
├──────────────────────────────────────┤
│ Free Space(动态)                     │  空闲空间
├──────────────────────────────────────┤
│ Page Directory(动态)                 │  页内目录(加速查找)
├──────────────────────────────────────┤
│ File Trailer(8字节)                  │  校验和(与File Header对应)
└──────────────────────────────────────┘
总大小:16KB

关键字段

  1. File Header

    • FIL_PAGE_OFFSET:页号
    • FIL_PAGE_PREV:前一页
    • FIL_PAGE_NEXT:后一页(形成双向链表)
  2. Page Directory

    • 页内槽(Slot),加速二分查找

5. 行(Row)

行格式

详见下一篇《InnoDB行格式:Compact、Redundant、Dynamic》。


存储层次关系

示例:users表

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    INDEX idx_name (name)
);

存储结构

表空间(users.ibd)
  ├─ 数据段(聚簇索引叶子节点)
  │   ├─ 区1(1MB = 64页)
  │   │   ├─ 页1(16KB):行1-200
  │   │   ├─ 页2(16KB):行201-400
  │   │   ├─ ...
  │   │   └─ 页64(16KB)
  │   └─ 区2(1MB)
  ├─ 索引段(聚簇索引非叶子节点)
  │   └─ 区3(1MB)
  └─ 索引段(idx_name二级索引)
      └─ 区4(1MB)

性能影响

1. 页大小与B+树高度

假设:
- 主键INT(4字节)
- 每个索引项14字节(主键4 + 页号6 + 其他4)
- 非叶子节点填充率67%

16KB页可以存储:
16384 × 67% / 14 ≈ 783个索引项

B+树高度计算:
- 高度1:1个根节点(索引页),1个叶子节点(数据页)
- 高度2:1个根节点 + 783个叶子节点 = 783 × 80(每页80行) ≈ 62,640行
- 高度3:1个根节点 + 783个分支节点 + 783×783个叶子节点 ≈ 4900万行

结论:3层B+树可以存储约5000万行数据!

2. 顺序IO vs 随机IO

顺序扫描(区为单位):
读取1MB(64页) = 1次磁盘寻址 + 顺序读取

随机读取(页为单位):
读取64页 = 64次磁盘寻址(慢10倍+)

实战建议

1. 使用独立表空间

# my.cnf
[mysqld]
innodb_file_per_table = 1  # 默认ON

2. 监控表空间大小

-- 查看表空间大小
SELECT
    table_name,
    ROUND(data_length/1024/1024, 2) AS data_mb,
    ROUND(index_length/1024/1024, 2) AS index_mb,
    ROUND((data_length + index_length)/1024/1024, 2) AS total_mb
FROM information_schema.TABLES
WHERE table_schema = 'test';

3. 回收空间

-- 删除数据后,空间未释放
DELETE FROM users WHERE id < 1000;
-- 表空间仍是原大小

-- 优化表,回收空间
OPTIMIZE TABLE users;
-- 或重建表
ALTER TABLE users ENGINE=InnoDB;

4. 分区表(大表优化)

-- 按范围分区
CREATE TABLE orders (
    id INT,
    order_date DATE,
    amount DECIMAL(10,2)
) PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p2025 VALUES LESS THAN (2026)
);

-- 每个分区一个独立表空间
orders#P#p2023.ibd
orders#P#p2024.ibd
orders#P#p2025.ibd

常见面试题

Q1: InnoDB的存储层次是什么?

  • 表空间 → 段 → 区(1MB) → 页(16KB) → 行

Q2: 为什么InnoDB的页大小是16KB?

  • 磁盘IO优化、缓存效率、B+树高度控制

Q3: 一个3层B+树可以存储多少行数据?

  • 约5000万行(假设每页80行)

Q4: 如何回收DELETE后的表空间?

  • OPTIMIZE TABLE 或 ALTER TABLE ENGINE=InnoDB

小结

表空间:最顶层,包含段(系统表空间、独立表空间) ✅ :索引段、数据段、回滚段 ✅ :1MB = 64页,减少碎片 ✅ :16KB,InnoDB的基本IO单位 ✅ :实际数据存储

理解InnoDB存储结构是性能优化的基础。


📚 相关阅读

  • 下一篇:《InnoDB行格式:Compact、Redundant、Dynamic》
  • 推荐:《Buffer Pool:MySQL的内存管理》