MySQL 索引原理:B+ 树结构与实战优化

在数据库查询中,磁盘 I/O 与内存操作的延迟差异巨大,通常相差数万倍。为了减少查询记录时的硬盘 I/O 次数,MySQL 采用了索引数据结构的设计。
1. 索引的减 I/O 设计
索引的核心思想是保持硬盘单次最大读取量(页)的最大化利用。每个 B+ 搜索树节点的存储空间就是一个页,每次读取完一个节点,就是一次完整的页存储内容加载。
2. 搜索树基础
2.1 方向与有序性
查询时,搜索树会避免全表遍历,而是沿着正确的范围、正向增长的方向进行搜索,直到精确匹配。同时,它维护了记录值以字段键的有序性,支持范围查询与排序。
对比哈希表:虽然哈希表能实现 O(1) 的精确匹配查询,但内部是无序数组,无法进行范围查询或排序。
2.2 多路分支设计
在一次读取的一个节点中,放置多个排布好的搜索对象进行分叉,能一次性处理更多对象,并缩小搜索区间。
2.2.1 B 树的局限
B 树将键值对存储在同一个节点中。如果节点空间有限(如一个页),且每条记录很大,那么一个节点能存的键值对数量就很少。这导致树的高度很高,大部分数据在叶子节点,需要多次从顶层读取到底层,I/O 开销大且不稳定。
2.2.2 B+ 树的优势
B+ 树针对上述问题进行了优化,将键与值分开存储:键在非叶子节点用于搜索,值在叶子节点对应。
非叶子节点:海量分叉
非叶子节点只存放无记录值的键字段。一个节点一个页可容纳多达 1600 个键字段,形成巨大的扇出能力。例如,3 层树就能覆盖亿级字段量,仅需 3 次硬盘读取。由于非叶子节点字段总量小,可以全部缓存到内存中。首次查询后,后续查询只需常数时间定位到叶子节点,再进行一次固定 I/O 即可获取数据。
叶子节点:稳定查询与范围扫描
所有数据最终都存储在叶子节点,形成全集键值对。更重要的是,叶子节点之间通过链表左右相连。这使得逻辑上的有序变成了物理相邻存储的有序。在进行范围查询时,避免了 B 树回溯节点的 I/O 开销,只需顺着链表顺序读取即可。此外,B+ 树只在叶子节点闭键,确保了查询时间开销的稳定性。

3. 索引的操作实践
3.1 查看与创建
使用标准 SQL 命令查看表中索引。为指定列创建索引时,主键、唯一键和外键会在建表时自动维护。
3.2 创建时机与风险
最好在表创建初期或数据量较小时建立索引。对于已有大量数据的表,直接创建索引风险极高。
想象一下,创建一个亿级数据的索引,系统需要从根节点出发,逐层分叉构建 B+ 树。随着层级加深,节点数量呈指数级增长。这个过程工作量巨大,可能导致服务器全盘负载过高甚至挂起。
3.3 大表索引的正确做法
如果必须为海量数据表添加索引,推荐的生产环境做法是:
- 在另一台 MySQL 服务器上创建相同结构的空表。
- 在该空表上创建好索引。
- 控制导入速度,将数据分批迁移到新表,确保索引正常维护。
- 最后切换流量至新表,完成平滑过渡。
删除索引同样需谨慎,应评估其对查询性能的影响后再执行。



