MySQL 页结构与数据存储原理全解析》

文章目录


《MySQL 页结构与数据存储原理全解析》


一、前言:理解页结构,是深入 InnoDB 的起点

大家好,我是程序员卷卷。

我们平时写 SQL 时,看到的都是逻辑上的"表""行""列",

但在 InnoDB 的底层,这一切都以 页(Page) 为单位进行物理存储与管理。

无论是索引查找、缓冲缓存、磁盘读写,最小单位都是"页"。

这意味着:

如果你真正理解了页的结构,就能从底层解释"为什么随机 IO 慢、为什么自增主键快、为什么行锁粒度能控制到单行"。


二、页的定义与基本单位

在 InnoDB 中:

  • 页(Page)是磁盘与内存交互的最小单位;
  • 每页默认大小为 16KB
  • 多个页组成区(Extent,1MB),多个区组成段(Segment),段组成表空间(Tablespace)。

层级结构如下:

复制代码
表空间(Tablespace)
 ├── 段(Segment)
 │     └── 区(Extent)
 │           └── 页(Page, 16KB)
 │                 └── 行(Row)

简单理解:

InnoDB 把磁盘空间分成了一个个"16KB 的小格子",每个格子就是一个 Page。


三、页的内部结构详解

每个页内部都包含固定的逻辑结构,可分为以下部分

复制代码
┌───────────────────────────┐
│ File Header(文件头)     │ 38字节,存页号、校验值等
│ Page Header(页头)       │ 56字节,页目录、行数等
│ Infimum/Supremum Record   │ 固定两条虚拟记录
│ User Records(用户记录)  │ 存储真实数据行
│ Free Space(空闲空间)     │ 插入新行时使用
│ Page Directory(页目录)   │ 记录偏移地址,加快查找
│ File Trailer(校验信息)   │ 校验页完整性
└───────────────────────────┘
(1)File Header(文件头)

记录页的编号、上一个页、下一个页、校验值,用于维护 B+Tree 的双向链表结构。

(2)Page Header(页头)

存储当前页中记录数量、最小和最大记录位置等元信息。

(3)Infimum/Supremum Record

两条虚拟记录,分别代表"最小值"和"最大值",

保证页内记录有序,便于二分查找。

(4)User Records(用户记录)

实际存储表中的数据行。

InnoDB 会根据主键顺序在页内排序,形成 B+Tree 的叶子节点结构。

(5)Free Space(空闲空间)

用于存放后续新增的行,当空间不足时会触发页分裂。

(6)Page Directory(页目录)

存储每条记录的偏移地址,用于加速定位。

查找记录时,InnoDB 先通过目录定位,再顺序扫描。


四、数据行在页中的存储方式

每一行数据在页中以"记录(Record)"形式存在,

每条记录包含两部分:记录头信息 + 实际列值

复制代码
┌───────────────────────────────┐
│ Record Header(记录头,5字节)│
│    + delete_flag              │
│    + next_record 指针         │
│    + record_type              │
│ Column Values(真实列数据)   │
└───────────────────────────────┘
举例说明

表结构:

sql 复制代码
CREATE TABLE user (
  id INT PRIMARY KEY,
  name VARCHAR(20),
  age INT
) ENGINE=InnoDB;

在页中表现为:

  • 页内记录根据 主键 id 有序排列;
  • 每条记录包含一个 "next_record" 指针,形成单向链表;
  • 通过页目录(Page Directory)加速二分查找;
  • 若页满,页会分裂(Page Split),形成新的页并更新 B+Tree 链接。

五、索引页与数据页的关系

InnoDB 的 B+Tree 索引中:

  • 叶子节点页 存放实际数据行(称为"数据页");
  • 非叶子节点页 存放索引键与指针(称为"索引页")。

示意图如下

复制代码
                 ┌─────────────────────┐
                 │     根节点页         │
                 └─────────┬───────────┘
                 ┌─────────┴───────────┐
        ┌────────▼────────┐    ┌────────▼────────┐
        │   索引页(中间层) │    │   索引页(中间层) │
        └────────┬────────┘    └────────┬────────┘
         ┌───────▼───────┐     ┌───────▼───────┐
         │   数据页(叶子层) │     │   数据页(叶子层) │
         └────────────────┘     └────────────────┘

所以 InnoDB 的主键索引即"聚簇索引",

数据行就存放在 B+Tree 的叶子节点中。


六、页分裂与合并

当插入新数据导致页满时:

  • InnoDB 会分裂出新页;
  • 重新分配部分记录;
  • 更新父节点指针。

页合并发生在删除大量数据时,

若相邻页利用率过低,会自动合并,减少碎片。

这两种操作是维护 B+Tree 平衡结构的关键。


七、面试高频问题与答题模板

问题 答案要点
Q1:InnoDB 一页多大?为什么是 16KB? 默认 16KB,兼顾 I/O 性能与内存命中率。
Q2:为什么 InnoDB 查找时要"回表"? 因为二级索引页存储主键值,需要到数据页(聚簇索引)获取完整行数据。
Q3:页目录的作用是什么? 存储记录偏移,加快页内定位。
Q4:页分裂是什么? 页空间不足时,数据部分迁移到新页,更新索引指针。
Q5:InnoDB 的叶子页与非叶子页区别? 叶子页存数据行,非叶子页存索引键与页号指针。
Q6:页是如何在内存与磁盘之间交互的? InnoDB Buffer Pool 以页为单位进行读写与缓存。

八、总结:理解页,就是理解 InnoDB

MySQL 的性能优化,本质上就是对"页"的利用优化。

  • 索引查找靠页定位;
  • Buffer Pool 按页缓存;
  • 磁盘 I/O 按页读写;
  • 表结构设计决定页利用率。

理解页结构,就能从物理层回答:

"为什么自增主键比随机主键更快?"

------因为自增主键插入不会频繁触发页分裂。

下一篇(第 4 篇),我将写------
《CHAR、VARCHAR、TEXT 的差别与存储方式》

带你理解字符串类型的底层存储差异、长度限制与性能影响。

相关推荐
hweiyu002 小时前
MySQL 从入门到精通(视频教程)
数据库·mysql
小跌—2 小时前
MySQL:数据库基础
数据库·mysql
张小洛2 小时前
Spring JDBC源码解析:模板方法模式的优雅实践
数据库·spring·模板方法模式·spring jdbc
SelectDB2 小时前
货拉拉用户画像基于 Apache Doris 的数据模型设计与实践
数据库·apache
weixin_387002152 小时前
漏洞修复学习之CVE-2024-10976漏洞复现
数据库·sql·学习·安全·postgresql
芒果要切2 小时前
SQL笔试题(2)
数据库·sql
robin59113 小时前
Linux-通过端口转发访问数据库
linux·数据库·adb
懒羊羊不懒@3 小时前
【数据库 | 基础】DDL语句以及数据类型
数据库
泷羽Sec-静安3 小时前
Less-9 GET-Blind-Time based-Single Quotes
服务器·前端·数据库·sql·web安全·less