深入解析:为什么 MySQL 采用 B+ 树作为索引
核心原因可以归结为:为了高效地适应磁盘存储和数据库查询模式。
下面我们从几个层面来详细拆解这个困难:
1. 与磁盘I/O的博弈:为什么不用二叉搜索树(BST)或红黑树?
问题所在:树的高度(Height)
二叉搜索树在理想情况下搜索时间复杂度是 O(log n),但这是在内存中的情况。数据库索引数据量巨大(几GB甚至TB级),无法全部装入内存,必须持久化到磁盘。
特别昂贵的执行就是磁盘I/O,比内存访问慢几个数量级。因此,减少磁盘I/O次数是设计数据库索引的首要目标。
“瘦高”的二叉树,每个节点最多只有2个子节点。当数据量很大时,树的高度会非常高。查找一个数据可能得访问很多个节点,就意味着需进行很多次磁盘I/O,效率极低。就是二叉搜索树或红黑树
B+ 树的解决方案:
一个“矮胖”的多路搜索树。一个节点行拥有就是B+ 树大量的子节点(称为“阶”,order)。这意味着:
极大地降低了树的高度。例如,一个深度为3的B+树,如果每个节点有1000个键,可以存储
1000 * 1000 * 1000 = 10亿条记录。只需要3次磁盘I/O就能找到所需的数据。每次磁盘I/O加载更多实用数据。磁盘读写是按“页”(Page,通常是4KB或更大)进行的,读取一个字节和读取一页的成本几乎一样。B+ 树的一个节点的大小被设计为恰好等于一页的大小,这样一次磁盘I/O就能完全加载一个包含大量键(和指针)的节点,充分利用了这次I/O的带宽。
2. 与兄弟B树的对比:为什么是B+树而不是B树?
多路平衡树,同样矮胖。但B+树在B树的基础上做了优化,这些优化对数据库场景至关重要。就是B树也
| 特性 | B树 | B+树 | 对数据库的优势 |
|---|---|---|---|
| 素材存储位置 | 每个节点都存储数据(key-value)。 | 只有叶子节点存储材料(value),非叶子节点只存储键(key)和子节点指针。 | 1. 查询效率更稳定:任何查询都必须走到叶子节点,路径长度相同,速度稳定。 2. 非叶子节点更小:同样大小的节点(一页),B+树的非叶子节点能存储更多键,从而进一步降低树的高度,减少I/O。 |
| 叶子节点结构 | 叶子节点是独立的。 | 所有叶子节点通过双向链表连接成一个有序链表。 | 范围查询性能极高。例如查询WHERE id BETWEEN 10 AND 50,B树需要做多次的中序遍历(回溯父节点),而B+树只需在叶子节点层找到10,然后沿着链表向右遍历即可,完全不需要回溯,性能巨幅提升。 |
| 全盘扫描 | 需要对树进行中序遍历。 | 直接遍历叶子节点的链表即可,非常高效。 | 适用于很多需要全表扫描的场景。 |
3. 与哈希索引的对比:为什么不用哈希表?
O(1),更快,但它有致命缺陷:就是哈希表的理论查询速度
无法支持范围查询:哈希表只能做等值查询(
=,IN),对于>,<,BETWEEN,ORDER BY等范围查询完全无能为力。无法利用索引排序:哈希索引的数据是散列的,没有顺序。
不支持最左前缀匹配:对于复合索引(多列索引),哈希必须计算所有列的整体哈希值,无法只使用索引的第一列。
而B+树的所有数据都在有序的叶子节点上,完美协助范围查询、排序和最左前缀原则。
总结:B+树的综合优势
在就是MySQL(InnoDB)选择B+树,是因为它磁盘I/O效率、范围查询性能、排序性能以及全表扫描效率之间取得最佳平衡的数据结构。
矮胖的树结构:极大减少磁盘I/O次数,适应海量数据存储。
叶子节点存储数据+链表结构:使得范围查询、排序、全表扫描变得极其高效,这是B树无法比拟的。
稳定的查询性能:每次查询都要遍历到叶子节点,耗时稳定。
充分利用磁盘预读特性:每次磁盘I/O读取一个页(节点)的数据,加载更多有效索引键。
补充一点:
MySQL的不同存储引擎协助不同的索引类型。例如,Memory存储引擎就支持哈希索引,缘于它适用于临时表、数据量小且完全在内存中的场景,这时哈希的O(1)查询速度更有优势。而InnoDB也支撑自适应哈希索引(Adaptive Hash Index),它会自动为频繁访问的B+树索引页在内存中创建哈希索引,来加速等值查询,但这只是一个内部优化,主索引结构依然是B+树。
所以,结论是:B+树是为磁盘-based的关系型数据库查询模式量身定做的最佳数据结构。
浙公网安备 33010602011771号