一、MySQL 为什么以"页"为最小存储单位?
InnoDB 最小存储单位不是行,而是 Page(页)
apache
1 Page = 16KB
- 为什么是 16KB?
| 维度 | 说明 |
|---|---|
| 磁盘 IO | 16KB 顺序读性价比最高 |
| 空间利用 | 兼顾碎片率和读写效率 |
| 内存管理 | Buffer Pool 换页高效 |
页是 MySQL 磁盘 IO 的最小单位,也是性能调优的根基
二、索引在磁盘上的真实结构
B+Tree 并不是逻辑结构,而是物理存储结构
apache
B+Tree 的每一个节点 = 一个 16KB 数据页
- 三层 B+Tree 真实形态:
css
Root Page
↓
Internal Page
↓
Leaf Page(索引页 / 数据页)
特点:
真正影响性能的是:叶子节点访问效率
三、页内部结构拆解
css
Page(16KB)
├── File Header
├── Page Header
├── Infimum + Supremum
├── User Records ← 行数据 / 索引数据
├── Free Space
└── Page Trailer
关键:
sql
User Records + Free Space = 可写入空间
四、页分裂(Page Split)------性能杀手
页分裂是 MySQL 写性能下降的最大元凶
- 什么是页分裂?
当页被写满后插入新数据:
插入新行
↓
页已满
↓
申请新页
↓
拆分一半数据
↓
页分裂发生
- 页分裂示意
makefile
原页: [1 2 3 4 5 6 7 8]
插入: 5.5
→ 分裂 →
页1: [1 2 3 4]
页2: [5 5.5 6 7 8]
- 页分裂带来的 4 大问题
| 影响 | 后果 |
|---|---|
| 申请新页 | 磁盘 IO 增多 |
| 数据移动 | 大量随机 IO |
| 页不连续 | 顺序读变随机读 |
| 树高增加 | 查询 IO 次数变多 |
页分裂 = IO 风暴 + 性能雪崩
五、页合并(Page Merge)
删除 ≠ 回收空间
-
什么时候发生页合并?
页利用率 < 50%
↓
触发合并 -
页合并示意
makefile
页A: [1 2]
页B: [3 4]
→ 合并 →
页A: [1 2 3 4]
- 页合并代价
频繁 delete 会导致系统性能不稳定
六、主键为什么必须单调递增?
这是 数据库索引设计的灵魂法则
-
自增主键插入流程
永远写入最右叶子节点
↓
顺序写磁盘
↓
几乎无页分裂 -
UUID 主键插入流程
随机定位页
↓
频繁页分裂
↓
随机 IO 激增 -
性能对比
| 主键类型 | 写入性能 |
|---|---|
| 自增ID | 5星 |
| UUID | 1星 |
主键选错,性能直接腰斩
七、索引高度 & 磁盘 IO 次数
树高 = 一次查询的磁盘 IO 次数
-
树高与 IO 对应关系
树高 1 → 1 次 IO
树高 2 → 2 次 IO
树高 3 → 3 次 IO -
数据规模 & 树高关系
| 数据量 | 树高 |
|---|---|
| 100万 | 2 ~ 3 |
| 1000万 | 3 |
| 1亿 | 3 ~ 4 |
亿级数据,一次查询通常 ≤ 4 次磁盘 IO
八、为什么索引字段要"短"?
假设:
索引项 20B → 1 页 ≈ 800 条
索引项 40B → 1 页 ≈ 400 条
结果:
索引变长 → 扇出减半 → 树高增加 → IO 增加
字段越短,性能越好
九、索引存储设计 5 大黄金法
markdown
1. 主键必须递增
2. 索引字段尽量短
3. 避免 UUID 主键
4. 控制索引数量
5. 用联合索引代替多个单列索引
十、面试官最爱 5 连问
十一、底层认知模型
页结构 → 页分裂 → IO 次数 → 树高度 → 查询性能
这条链路,决定了 MySQL 性能上限
源码地址
1、https://pan.baidu.com/s/1B6pgLWfSgMngVeFfSTcPdg?pwd=jc1s
如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!