【MySQL篇|第九篇】MySQL为什么要使用B+树

MySQL的InnoDB存储引擎采用B+树作为索引结构,主要因其能最小化磁盘I/O次数:非叶子节点仅存索引键,提升扇出度、降低树高;叶子节点有序链表天然支持高效范围查询与顺序扫描;所有查询路径长度一致,保障性能稳定;且与聚簇索引设计深度契合。相比B树,B+树在海量数据下的I/O效率、范围查询和全表扫描性能更优。

一、什么是 B+ 树

B+ 树是一种为磁盘等外存储设备设计的多路平衡查找树,是 B 树的变体。它在数据库索引和文件系统中被广泛使用,核心目标是在海量数据下把查找所需的磁盘 I/O 次数降到最低。

理解 B+ 树,可以先抓住它与 B 树最本质的几点区别:

  • 数据只存在叶子节点:非叶子节点只保存索引键(key)和指向子节点的指针,不保存实际数据行,因此单个节点能容纳更多的键,树的高度更低。
  • 叶子节点之间用链表相连:所有叶子节点按 key 有序排列,并通过双向链表串联,天然支持范围查询和顺序遍历。
  • 所有叶子节点在同一层:B+ 树是严格平衡的,从根到任意叶子的路径长度相同,查询性能稳定。
  • 非叶子节点的 key 是子树的“路标”:一个 key 在非叶子节点中出现后,仍会出现在叶子节点中,非叶子节点只起导航作用。

1.1 结构示意

下面是一棵简化后的 B+ 树结构,非叶子节点只做路由,数据全部落在叶子层:

1.2 查找过程

以查找 key = 62 为例:

  1. 从根节点开始,62 大于 50 且小于 70,进入左边这个子树。
  2. 在内部节点 50 | 65 中,62 大于 60,进入中间侧子节点。
  3. 到达叶子节点 60,62,在其中定位到 62。

整个过程只访问了 3 个节点,也就是 3 次磁盘 I/O。速度十分迅速,同时也能够最大程度上节省cpu的性能。

1.3 与 B 树的对比

下面是B树的照片:

对比项B 树B+ 树
数据存放位置所有节点都可能存放数据只有叶子节点存放数据
单节点可容纳的 key 数量较少(数据占用空间)较多(只存 key 和指针)
树的高度相对更高相对更低
范围查询需要中序遍历,回溯较多叶子链表顺序扫描,效率高
查询性能命中位置不同,I/O 次数不稳定每次都要走到叶子,I/O 次数稳定

二、MySQL 为什么使用 B+ 树

MySQL 的 InnoDB 存储引擎使用 B+ 树作为索引结构,这不是随意选择,而是由磁盘特性、数据规模和 SQL 查询模式共同决定的。

2.1 磁盘 I/O 是性能瓶颈

数据库的数据最终存放在磁盘上,磁盘随机读取的耗时远高于内存访问。一次磁盘寻道通常在毫秒级,而内存访问是纳秒级,两者相差几个数量级。因此索引结构的设计目标不是“比较次数最少”,而是磁盘 I/O 次数最少。

B+ 树的每个节点大小通常与磁盘页(InnoDB 默认 16KB)对齐,一次 I/O 就能读入一个完整节点。节点内使用二分查找,节点间通过多路分支降低树高,从而把 I/O 次数压到很低。但是B树非叶子节点也存储对应的数据会加大找寻数据的难度,树的高度也会变得更高一些,也就是会加大cpu的功耗。

2.2 非叶子节点不存数据,树更“矮胖”

假设 InnoDB 页大小为 16KB,主键为 BIGINT(8 字节),加上页内指针约 6 字节,一个非叶子节点大约能存放 16KB / 14B ≈ 1170 个 key。若每个叶子节点能存放约 16 条数据行,那么一棵高度为 3 的 B+ 树可以索引:

1170 × 1170 × 16 ≈ 2190 万行

也就是说,两千万级别的数据表,通过主键查询通常只需要 3 次磁盘 I/O。如果非叶子节点也存放数据行,单节点能容纳的 key 会大幅减少,树高随之增加,I/O 次数也会上升。

2.3 叶子链表天然支持范围查询

实际业务中,范围查询和排序非常常见,例如分页、时间区间筛选、ORDER BY、BETWEEN 等。B+ 树的叶子节点有序且相互连接,定位到起始位置后顺序扫描即可,避免了 B 树中频繁的中序回溯。

-- 定位到 100 后沿叶子链表顺序读取,效率很高
SELECT * FROM orders WHERE id BETWEEN 100 AND 200;

-- 按索引列排序时可直接利用叶子节点的有序性
SELECT * FROM orders ORDER BY id LIMIT 20;

2.4 查询性能稳定

B+ 树的所有数据都在叶子层,任何一次等值查询都必须走到叶子节点,路径长度一致。这意味着查询的时间复杂度稳定在 O(log n),不会出现某些 key 很快、某些 key 很慢的情况,便于容量评估和性能调优。

2.5 更适合全表扫描和顺序访问

由于数据全部集中在叶子层且按序排列,全表扫描或全索引扫描时只需顺序遍历叶子链表,磁盘预读(read-ahead)能发挥很好的效果,连续 I/O 的效率远高于随机 I/O。

2.6 与 InnoDB 的聚簇索引设计契合

InnoDB 的主键索引是聚簇索引,叶子节点直接存放整行数据;二级索引的叶子节点存放主键值,查询时再回表。这种设计正是建立在 B+ 树“数据只在叶子层”的前提之上:

  • 聚簇索引:叶子节点即数据页,主键查询一次 B+ 树即可拿到整行。
  • 二级索引:叶子节点存主键,先查二级索引再回表查聚簇索引,结构清晰、职责分明。

2.7 为什么不用其他结构

数据结构主要问题
二叉搜索树 / AVL / 红黑树每个节点只有两个分支,树高过大,磁盘 I/O 次数多
B 树非叶子节点存数据,扇出小、树更高,范围查询效率低
哈希表只支持等值查询,不支持范围查询和排序,且无法顺序扫描
跳表内存结构表现优秀,但磁盘上节点分散,I/O 不友好
LSM 树写性能好,但读放大明显,适合写多读少场景,与 OLTP 主键索引诉求不同

三、小结

B+ 树通过“非叶子节点只存索引、数据集中在叶子层、叶子节点链表相连”的设计,在磁盘 I/O 次数、范围查询能力和查询稳定性之间取得了很好的平衡。MySQL 选择 B+ 树,本质上是因为它最契合磁盘存储的特性与关系型数据库的查询模式:树足够矮,I/O 足够少,范围扫描足够快,性能足够稳定。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值