在 MySQL 的面试中,有一个问题几乎成了“保留曲目”:为什么 MySQL 的 InnoDB 引擎选择 B+ 树作为索引结构,而不是 B 树? 很多人能背出“B+ 树矮胖、IO 少、范围查询快”这样的结论,但一旦面试官追问“具体为什么”,往往就卡壳了。
这篇文章将带你从底层原理出发,彻底搞懂 B+ 树和 B 树的本质区别,以及为什么 B+ 树才是数据库索引的最优解。
一、先搞懂:B 树和 B+ 树到底长什么样
在讨论“为什么”之前,我们必须先明确“是什么”。
B 树(B-Tree) 是一种多路平衡查找树。它的每个节点既存储键(key),也存储数据(data),并且所有节点都包含指向子节点的指针。也就是说,数据分散在整棵树的每一个节点中。
B+ 树(B+Tree) 是 B 树的变种,它做了两个关键改动:
- 非叶子节点只存储键,不存储数据,数据全部放在叶子节点上。
- 所有叶子节点通过指针连成一个有序链表。
这两个改动看似简单,却直接决定了 B+ 树在数据库场景下的统治地位。
二、核心原因一:磁盘 IO 次数更少
数据库索引是存储在磁盘上的,而磁盘 IO 是数据库性能的最大瓶颈。因此,衡量一个索引结构好坏的核心指标,就是查找一条数据需要多少次磁盘 IO。
磁盘 IO 的次数取决于树的高度。树越矮,IO 越少。
那么树的高度由什么决定?由每个节点能容纳多少个键决定。节点能容纳的键越多,单次 IO 加载的信息量越大,树就越矮。
关键来了:
- 在 B 树中,每个节点既要存键,又要存数据。假设一个节点大小为 16KB(InnoDB 默认页大小),如果数据本身很大,那么能存的键就很少,树就会变高。
- 在 B+ 树中,非叶子节点只存键和指针,不存数据。这意味着同样 16KB 的节点,B+ 树能存下远多于 B 树的键。
举个例子:假设键占 8 字节,指针占 6 字节,数据占 1KB。
- B 树非叶子节点:每个键值对大约 8 + 6 + 1000 ≈ 1014 字节,16KB 只能存约 16 个键。
- B+ 树非叶子节点:每个键值对只有 8 + 6 = 14 字节,16KB 能存约 1170 个键。
同样是三层的树:
- B 树:16 × 16 × 16 ≈ 4096 条数据。
- B+ 树:1170 × 1170 × 1170 ≈ 16 亿条数据。
结论:B+ 树能在同样的磁盘 IO 次数下,索引远远多于 B 树的数据量。换句话说,查找同样多的数据,B+ 树的树高更低,磁盘 IO 更少,性能更高。
三、核心原因二:范围查询效率碾压 B 树
数据库中大量的查询都是范围查询,比如:
SELECT * FROM orders WHERE create_time BETWEEN '2024-01-01' AND '2024-01-31';
这种查询在 B+ 树上效率极高,原因就在于叶子节点的链表结构。
在 B+ 树中,所有数据都在叶子节点,并且叶子节点之间用指针连成了一个有序双向链表。当我们要查一个范围时:
- 先通过树查找找到范围的起始叶子节点。
- 然后沿着叶子节点的链表顺序向后遍历即可。
整个过程是顺序 IO,非常高效。
而在 B 树中,数据分散在各个层级的节点上。要做一个范围查询,必须中序遍历整棵树,在节点之间来回跳跃,产生大量随机 IO。随机 IO 的性能远低于顺序 IO,这是数据库性能的致命伤。
结论:B+ 树的链表结构让范围查询和排序操作变得极其高效,而 B 树在这方面先天不足。
四、核心原因三:查询性能更稳定
在 B 树中,数据可能出现在任何一层节点上。如果运气好,在根节点就找到了数据;如果运气差,要走到很深的叶子节点。这意味着每次查询的 IO 次数是不确定的,性能波动大。
而在 B+ 树中,所有数据都在叶子节点,每次查询都必须走到叶子节点,路径长度一致。因此,每次查询的 IO 次数是稳定的,性能可预测。
对于数据库这种需要稳定响应时间的系统来说,可预测性至关重要。
五、核心原因四:更适合磁盘预读和缓存
磁盘读取有一个特性:每次读取的最小单位是一个页(通常 4KB 或 16KB),而不是一个字节。这就是所谓的“磁盘预读”。
B+ 树的非叶子节点只存键和指针,节点“更瘦”,一个磁盘页能装下更多的键。这意味着:
- 同样的磁盘页大小,B+ 树能覆盖更大的索引范围。
- 非叶子节点更容易被完整加载到内存中缓存,减少后续 IO。
B 树因为节点“更胖”,一个页装不下几个键,缓存效率更低。
六、一张表总结 B 树 vs B+ 树
| 对比维度 | B 树 | B+ 树 |
|---|---|---|
| 数据存储位置 | 所有节点都存数据 | 只有叶子节点存数据 |
| 树的高度 | 较高 | 较低 |
| 磁盘 IO 次数 | 较多 | 较少 |
| 范围查询 | 需中序遍历,随机 IO | 叶子链表,顺序 IO |
| 查询性能 | 不稳定 | 稳定 |
| 缓存友好度 | 较低 | 较高 |
七、面试怎么答才出彩
如果面试官问“为什么 MySQL 用 B+ 树而不是 B 树”,你可以这样组织回答:
核心原因有三点。第一,B+ 树的非叶子节点不存数据,同样大小的磁盘页能存更多键,树更矮,磁盘 IO 更少。第二,B+ 树叶子节点用链表连接,范围查询和排序可以顺序遍历,效率远高于 B 树的中序遍历。第三,B+ 树所有查询都要走到叶子节点,IO 次数稳定,性能可预测。此外,B+ 树对磁盘预读和缓存也更友好。
这样回答,既有结论,又有原理,还有对比,面试官想不满意都难。
结语
B+ 树并不是凭空被选中的,它是为数据库“磁盘存储 + 大量范围查询 + 稳定性能”这些需求量身定制的数据结构。理解了 B 树和 B+ 树的本质区别,你不仅能在面试中游刃有余,更能在实际开发中做出更合理的索引设计决策。
下次再遇到这个问题,别再只背结论了,把原理讲清楚,才是真正的加分项。
未经允许不得转载:任鹏个人博客 » MySQL 面试必问:B+ 树索引为什么比 B 树更适合数据库

