核心设计思想:以空间换时间
索引本质上是一种排好序的数据结构。它的存在是为了减少磁盘 IO 次数,让你从几千万行数据中“定位”而不是“扫描”。
一、 核心组件:B+ Tree(最强导航仪)
虽然索引可以用 Hash、二叉树等实现,但 InnoDB 选择了 B+ Tree。
-
数据结构特点:
- 非叶子节点:只存索引(Key),不存数据,这样一页能塞下更多路径,树会很“矮”。
- 叶子节点:存所有数据,并且叶子之间有双向链表。
- 高度低:千万级的数据,树的高度通常只有 3-4 层,意味着只需 3-4 次磁盘 IO。
二、 索引类型对比:聚簇 vs 二级
这是最容易混淆的地方,我们用“身份证”来做对比。
| 维度 | 聚簇索引 (Clustered) | 二级索引 (Secondary) |
|---|---|---|
| 存放内容 | 索引 + 完整的行数据 | 索引 + 主键 ID |
| 数量限制 | 一张表只能有一个(通常是主键) | 可以有多个(如姓名、手机号) |
| 查询效率 | 最高(一次定位直接拿货) | 较低(需先找主键,再回主键树找数据) |
| 形象理解 | 字典正文(按拼音排好序的字) | 字典后的部首检字表(只告诉你页码) |
三、 索引的优势与代价(利弊分析)
任何技术都有两面性,索引也不是越多越好。
1. 优势(为什么用?)
- 极速查询:将全表扫描变为“点对点”定位。
- 降低排序成本:索引本身就是有序的,执行
ORDER BY时直接取结果,不再消耗 CPU 排序。 - 保证唯一性:唯一索引能自动帮业务层检查数据重复。
2. 代价(副作用)
- 占用空间:索引也是文件,存放在磁盘上,表越大索引越占空间。
- 降低写速度:你每
INSERT或UPDATE一条数据,存储引擎都要去维护一遍 B+ Tree 的顺序。
四、 核心对比:为什么不用其他结构?
通过对比,你就能明白 MySQL 设计者的良苦用心:
-
对比 二叉树:二叉树每个节点只能有两个子节点。当数据量达到千万级时,树的高度会变得非常夸张(可能达到几十层)。树越高,磁盘 IO 次数越多,查询就会慢得像蜗牛。
-
对比 Hash 索引:Hash 表利用哈希算法定位,找单个值极快()。但它致命的缺点是不支持范围查询(比如查
id > 100)和排序。因为经过 Hash 计算后,数据的原始顺序已经全部打乱了。 -
对比 B 树 (B-Tree) :
- 痛点 1:空间利用率低。B 树的每个节点(包括根和中间节点)都会存储整行数据。这导致一个 16KB 的页能存的索引数量变少,树会比 B+ 树更高,增加 IO 次数。
- 痛点 2:范围查询低效。B 树没有叶子节点之间的链表,做范围查询时需要不断地在树的层级间“回溯”遍历,性能不稳定。
-
最终选择 B+ Tree:
- 更矮胖:中间节点只存索引不存数据,一页能存更多指针,千万级数据仅需 3~4 层。
- 更高效:叶子节点不仅存了全量数据,还用双向链表串联了起来。找范围时,只需定位起点,横向一拉,数据全出。
五、 避坑指南:什么情况下索引会“失效”?
即使建了索引,如果 SQL 写得不好,服务层的优化器也会放弃索引。
- 违背最左匹配:复合索引
(name, age),你只查age,索引不理你。 - 函数操作:
WHERE ABS(id) = 1,给索引列套函数,索引失效。 - 模糊开头:
LIKE '%张',前缀模糊无法利用 B+ Tree 的有序性。
📝 本章总结:给读者的“加深思考”
“索引不是越多越好,而是越准越好。”
- Server 层(优化器) :负责看你的 SQL,决定走哪个索引。
- 引擎层(InnoDB) :负责维护 B+ Tree,并在执行器的指令下,顺着树根找到那页数据。