主题
概念卡片:MySQL 索引与 B+ 树
一句话机制
InnoDB 用 B+ 树做索引:主键索引(聚簇索引)的叶子存整行数据,二级索引的叶子存主键值;用二级索引查数据要先查二级树拿到主键、再「回表」查主键树。 索引的本质是用「多叉树降低树高」,让一次查询尽量少读磁盘。
为什么是 B+ 树(不是二叉树)
| 索引模型 | 等值查询 | 范围查询 | 更新 | 适用 |
|---|---|---|---|---|
| 哈希表 | 快 | 慢(无序,全扫) | 快 | 等值场景(Memcached/NoSQL) |
| 有序数组 | 快(二分) | 快 | 慢(插入要挪) | 静态数据 |
| 二叉搜索树 | O(logn) | — | — | 树太高,读磁盘多 |
| B+ 树(N 叉) | O(logn) | 快 | 快 | 数据库标配 |
二叉树 100 万节点树高 20,一次查询要读 20 个数据块。B+ 树是 N 叉(InnoDB 的 N≈1200),树高 4 就能存 1200³≈17 亿,查一个值最多读 3 次磁盘——这就是「多叉降低树高、减少磁盘 IO」的核心。
聚簇索引 vs 二级索引
| 类型 | 叶子存什么 | 别名 |
|---|---|---|
| 主键索引 | 整行数据 | 聚簇索引(clustered index) |
| 非主键索引 | 主键值 | 二级索引(secondary index) |
回表:select * from T where k=5 用二级索引查,先搜 k 索引树拿到主键 id=500,再回主键索引树搜一次拿整行——多扫一棵树。
避免回表的手段:覆盖索引(查询的字段都在二级索引里,不用回表)+ 尽量用主键查询。
页分裂与页合并
- 页分裂:B+ 树插入中间值,数据页满了要申请新页挪数据,空间利用率降约 50%
- 页合并:相邻两页因删除利用率低时合并
自增主键 vs 业务主键
| 维度 | 自增主键 | 业务主键(如身份证号) |
|---|---|---|
| 插入 | 追加操作,不触发页分裂 | 无序插入,页分裂频繁 |
| 二级索引大小 | 主键 4 字节(int)/8 字节(bigint) | 主键可能 20 字节(字符串) |
主键越短,二级索引叶子越小、越省空间 → 常规业务优先自增整型主键。业务主键只适合「只有一个索引 + 必须唯一」的 KV 场景(无二级索引,不用管叶子大小)。
常见误解(避坑)
- ❌ "索引存在内存里"。→ 索引要写磁盘,所以选多叉树而非二叉树来降低树高、减少磁盘 IO。
- ❌ "主键和普通索引查起来一样快"。→ 普通索引要回表,多扫一棵主键树。
- ❌ "主键随便选个字符串就行"。→ 主键长度会放大到每个二级索引的叶子,字符串主键让所有二级索引变大。
- ❌ "索引越细越好"。→ 索引有维护成本(页分裂、写放大),只在查询多、写入少的列建。
关联
- 原始资料:04_深入浅出索引(上) · 05_深入浅出索引(下)
- 总览:MySQL技术栈总览
- 相关卡:面试题蒸馏卡:数据库索引失效定位(索引失效场景)
- 域地图:A00-百科/数据与存储/数据与存储