mysql为什么用 B+ 树而不用 B 树呢?
MySQL(尤其是其默认存储引擎InnoDB)选择 B+树 而非 B树 作为索引的核心数据结构,是经过深度权衡后的结果。核心原因围绕 磁盘I/O效率、范围查询性能 和 与数据库特性的契合度 展开。以下是具体分析:
1. 更优的磁盘 I/O 性能(核心优势)
B+树非叶子节点不存储数据
B+树的内部节点仅存储键值和子节点指针,而 B树的每个节点都存储键值+数据指针(甚至直接存储行数据)。
影响:在相同磁盘页大小(如16KB)下,B+树的非叶子节点能存储更多键值(扇出更大)。
树高显著降低(3~4层即可支持千万级数据),查询时磁盘I/O次数更少(减少随机I/O)。
示例:
假设一个节点大小16KB,键值占10B,指针占6B:
B+树:一个非叶子节点可存
16KB / (10B+6B) ≈ 1000
个键值。B树:若数据指针占8B,则只能存
16KB / (10B+6B+8B) ≈ 666
个键值。
B+树的树高更低,查询路径更短!
2. 范围查询的极致优化
B+树叶子节点形成有序链表
B+树的所有叶子节点通过双向指针串联,天然支持顺序扫描。
范围查询(如
WHERE id BETWEEN 100 AND 200
):定位到
id=100
的叶子节点。沿链表顺序遍历,直到
id>200
,全程顺序I/O。B树的缺陷:
数据分散在各层节点,范围查询需回溯父节点+深度遍历,产生大量随机I/O。
3. 查询稳定性(可预测性)
B+树:所有查询必须到达叶子节点
无论查哪条数据,路径长度严格等于树高(性能稳定)。B树:数据可能出现在中间节点
若目标数据在根节点附近,查询更快;若在叶子节点,则需完整遍历。
影响:B树的查询时间波动大,不利于数据库优化器预判代价。
4. 全表扫描效率
B+树:遍历叶子节点链表即可完成全表扫描(顺序I/O)。
B树:需中序遍历整棵树(递归访问左/中/右节点),随机I/O多,速度慢。
5. 与InnoDB存储设计的深度契合
聚集索引(Clustered Index):
InnoDB的表数据直接存储在B+树叶子节点(按主键排序),形成"索引即数据"的结构。二级索引(Secondary Index):
叶子节点存储主键值(非数据指针),通过主键回表查询。
B+树的"所有数据在叶子节点"特性完美支持此设计:主键索引:叶子节点=行数据。
二级索引:叶子节点=主键值 → 减少冗余存储。
对比总结:B+树 vs B树在MySQL中的表现
特性 | B树 | B+树 | B+树优势 |
---|---|---|---|
节点数据 | 所有节点存键值+数据指针 | 非叶节点仅存键值+指针;叶子节点存数据 | 非叶节点更小 → 树高更低 |
范围查询 | 需回溯中序遍历(随机I/O) | 叶子节点链表顺序扫描(顺序I/O) | 速度提升10倍+ |
查询稳定性 | 不稳定(数据可能在任意层) | 稳定(必须到叶子节点) | 优化器代价估算更准确 |
全表扫描 | 中序遍历(随机I/O) | 遍历叶子链表(顺序I/O) | 避免大量随机I/O |
缓存利用率 | 节点含数据 → 缓存键值更少 | 非叶节点纯键值 → 更多节点缓存 | 热数据常驻内存,减少磁盘访问 |
适配InnoDB | 难以实现"索引即数据" | 天然支持聚集索引+二级索引架构 | 深度契合数据库引 |
为什么B树没有被完全淘汰?
虽然B+树在磁盘数据库中占绝对优势,但B树仍有场景:
内存数据库(如Redis):数据在内存中,无需优化磁盘I/O,B树的单点查询可能更快。
特定文件系统(如HFS+):元数据索引场景中B树仍有应用。
但在MySQL这类基于磁盘的关系型数据库中,B+树是索引的最优解。
总结:MySQL选择B+树,本质是以磁盘I/O最小化为核心,通过降低树高、顺序访问优化、稳定查询路径三大设计,完美适配数据库的高并发、大数据量、范围查询等核心需求。