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

相关推荐
java1234_小锋7 小时前
【技术专题】Mysql8 数据库 - Mysql8 数据类型简介
数据库·mysql
JosieBook7 小时前
【数据库】MySQL 实战精通系列 · 第10篇:分库分表与分布式事务实战
数据库·分布式·mysql
做运维的阿瑞11 小时前
多表查询基础:INNER JOIN、LEFT JOIN、RIGHT JOIN
数据库·sql·mysql·算法
夜雪一千13 小时前
MySQL 中什么是条件注释
数据库·mysql
暖核13 小时前
MySQL 高级运维核心:备份恢复、主从复制与 MHA 高可用复习总结
运维·数据库·mysql
晚风叙码15 小时前
MySQL 表的操作:从建表到删表,一篇讲清楚
android·mysql·oracle
paopaokaka_luck15 小时前
非遗文物数字化小程序(AI非遗问答、ONNX图像识别、协同过滤推荐、ECharts数据分析、非遗知识浏览与互动、文创商城订单闭环、文化活动报名签到、社区交流)
javascript·spring boot·mysql·数据分析·echarts·mybatis
Dxy123931021616 小时前
MySQL 条件注释 vs 普通注释
数据库·mysql
做运维的阿瑞16 小时前
自连接、交叉连接与复杂 JOIN:一条问题链讲透
前端·数据库·mysql
小马同学-17 小时前
MySQL5.x源码安装
mysql