InnoDB 存储引擎的逻辑存储结构深度解析

在 MySQL 的几大存储引擎中,InnoDB 凭借事务支持、行级锁以及崩溃恢复等特性成为默认并最常用的引擎。要真正理解 InnoDB 的性能特性及其行为(如锁、索引、磁盘 IO、事务隔离),必须先搞清楚其 逻辑存储结构

本文从最顶层的表空间,到最底层的记录行,分层介绍 InnoDB 的逻辑结构如何设计、各层的作用是什么、能帮助你理解哪些数据库现象。


1. InnoDB 的逻辑结构总览

InnoDB 按照"由大到小"可以分为四层:

  1. 表空间(Tablespace)

  2. 段(Segment)

  3. 区(Extent)

  4. 页(Page)

  5. 行(Row)

结构示意如下:

复制代码
Tablespace
  ├── Segment(数据段/索引段/回滚段)
        ├── Extent(64 个页组成)
              ├── Page(16KB 默认)
                    └── Row(记录)

这些层次并非孤立存在,而是为了解决磁盘管理、索引存储、并发访问等问题而设计的。


2. 表空间(Tablespace)

表空间是 InnoDB 逻辑存储结构的最顶层概念。常见的几类表空间包括:

  • 系统表空间(system tablespace)

  • 独立表空间(file-per-table tablespace)

  • 临时表空间(temporary tablespace)

  • Undo 表空间(undo tablespace)

从 MySQL 5.6 开始,一般都推荐使用 "独立表空间 ":每个表独立一个 .ibd 文件,便于管理和迁移。

表空间内部存放各种段(Segment),段继续存放区(Extent),区再存放页(Page)。


3. 段(Segment)

段是 InnoDB 中"对象级别"的存储单位,常见的段包括:

  • 数据段(Data Segment) → 存放 B+Tree 的叶子节点页

  • 索引段(Index Segment) → 存放 B+Tree 的非叶节点页

  • 回滚段(Rollback Segment) → 用来存储 Undo Log

一个表的主键索引是一个聚簇索引(Clustered Index),其叶子节点存储整行数据,因此一个聚簇索引至少包括:

  • 一个数据段(叶子节点)

  • 一个索引段(非叶子节点)

二级索引(Secondary Index)也有同样的段结构,只是叶子节点存放的是索引键 + 主键。

段的特点是:动态申请区(Extent) 来扩容,以 64 页为单位增长,保证磁盘分配的连续性与性能。


4. 区(Extent)

区是 InnoDB 空间分配的基本单位,每个区包含:

64 个页(Page)

默认页大小为 16KB,因此一个区大小为:

复制代码
64 * 16KB = 1MB

区的类型:

  • 空闲区(FREE)

  • 部分使用区(FREE_FRAG)

  • 全满区(FULL)

  • 用于段(Segment)的区(ALLOCATED)

区的存在意义是:

  • 避免频繁的小文件碎片化

  • 提升顺序 IO 性能(B+Tree 叶子页被尽量按顺序放在同一或连续区中)

  • 方便快速扩表


5. 页(Page)

InnoDB 的核心单位是 页(Page)。这是存储的最小分配单位。

常见的页类型:

PageType 作用
B-tree Node (0x45BF) B+Tree 索引页(最重要)
Undo Log Page Undo 日志
INODE Page 段的元数据
Index Page 叶子节点和非叶节点页
Data Page 行数据存储区
FSM Page 空闲空间管理

默认页大小为 16KB,可通过 innodb_page_size 调整(4KB / 8KB / 16KB)。

页内部结构(以索引页为例):

复制代码
+-------------------------------+
| File Header (38 bytes)        |
+-------------------------------+
| Page Header (56 bytes)        |
+-------------------------------+
| Infimum/Supremum 虚拟行       |
+-------------------------------+
| 用户记录(Row)               |
+-------------------------------+
| Free Space                    |
+-------------------------------+
| Page Directory                |
+-------------------------------+
| File Trailer (8 bytes)        |
+-------------------------------+

其中"Infimum/Supremum" 是页内 B+Tree 的边界记录。


6. 行(Row)结构

一条行记录的真实存储方式与我们在 SQL 中看到的不同。为了支持 MVCC、锁、索引等功能,InnoDB 会对行做特殊处理。

一行存储包括:

  • 真实列数据

  • 隐藏列

    • DB_TRX_ID(6 bytes):最近修改该行的事务 ID

    • DB_ROLL_PTR(7 bytes):指向 Undo Log

    • DB_ROW_ID(如果没有主键才自动生成)

行格式主要包括:

  • Compact(最常用)

  • Redundant(老格式)

  • Dynamic(TEXT/BLOB 溢出存储)

  • Compressed(页压缩)

如果行中包含大型字段(如 TEXT、BLOB),可能会被分拆到:

  • 主记录页存储 20 字节指针

  • 数据实际存放在溢出页(Overflow Page)

这也是查询大字段性能差的原因之一。


7. 小结:InnoDB 的逻辑结构如何影响性能?

理解这些结构可以帮助你分析实际问题。

例如:

  • 为什么查询主键比二级索引快?

    → 因为聚簇索引叶子节点就存整行,二级索引需要回表。

  • 为什么大表插入时可能出现性能跳变?

    → 因为段要扩容,申请新的区。

  • 为什么页分裂会导致性能下降?

    → 页被写满后需要拆裂,B+Tree 维护成本变高。

  • 为什么 delete 后表文件不缩小?

    → 页被标记为 Free,但不会自动回收空间,仍属于表空间。


结语

InnoDB 的逻辑结构是理解 MySQL 底层行为的关键。

从表空间、段、区、页再到具体的记录行,每一层结构都有其强烈的设计目的:提高索引效率、减少碎片、增强磁盘顺序访问、实现 MVCC 与事务隔离。

理解这些结构,不仅能帮助你更好地调优 SQL,还能在面对复杂问题(锁等待、慢查询、空间膨胀)时做出更准确的判断。

相关推荐
张洪权2 小时前
RBAC 菜单查询的“标准写法”
mysql
乘风!3 小时前
服务器上部署的Mysql,服务器上可登录成功,远程电脑无法连接的
mysql
梓沂3 小时前
解决项目容器启动时MySQL端口检测的问题
数据库·mysql
soft20015254 小时前
MySQL 8.0.39 Rocky Linux 一键安装脚本(完整可直接运行)
linux·mysql·adb
木风小助理4 小时前
子查询与 JOIN 查询性能比较:执行机制与适用场景解析
数据库·sql·mysql
九章-4 小时前
智慧文旅信创落地新标杆:四川省文旅厅完成MySQL 5.7平滑替换,筑牢省级管理平台自主可控底座
数据库·mysql
蟹至之4 小时前
【MySQL】事务
数据库·mysql·事务
ao_lang4 小时前
数据库范式
数据库·mysql
子超兄5 小时前
MVCC机制简介
数据库·mysql