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,还能在面对复杂问题(锁等待、慢查询、空间膨胀)时做出更准确的判断。

相关推荐
To Be Clean Coder16 分钟前
【Spring源码】getBean源码实战(三)
java·mysql·spring
IT技术分享社区24 分钟前
数据库实战:MySQL多表更新JOIN操作的底层原理与性能调优指南
数据库·mysql·程序员
UrSpecial1 小时前
InnoDB存储引擎
数据库·mysql
gjc5922 小时前
MySQL隐蔽 BUG:组合条件查询无故返回空集?深度排查与规避方案
android·数据库·mysql·bug
Micro麦可乐2 小时前
分词搜索必须上Elasticsearch?试试MySQL分词查询,轻松满足大多数搜索场景的需求
大数据·mysql·elasticsearch·分词搜索·分词查询
a187927218312 小时前
MySQL 事务
数据库·mysql·事务·mvcc·acid·readview·可见性判断算法
陌路203 小时前
MYSQL事务篇--事务隔离机制
数据库·mysql
·云扬·4 小时前
MySQL Group Replication(MGR)核心特性全解析:从事务流程到一致性配置
数据库·mysql
陌路204 小时前
MYSQL事务篇--事务隔离机制的实现
数据库·mysql
oMcLin5 小时前
CentOS 7.9 高负载导致 MySQL 数据库性能下降:内存泄漏与配置优化
数据库·mysql·centos