MySQL 索引原理系列(二·补充):MySQL 为什么不用红黑树,而用 B+树?

上一篇我们讲清楚了 B树和 B+树之间的区别:

  • B树的所有节点都可能存储数据;
  • B+树的非叶子节点主要负责导航;
  • 数据集中存放在叶子节点;
  • 叶子节点按照索引顺序组织,适合连续扫描。

但这里很容易产生一个新的疑问:

红黑树的查询效率也很高,Java 中的 TreeMap、Linux 中的一些数据结构也会使用红黑树,为什么 MySQL 索引不使用红黑树,而要使用看起来更加复杂的 B+树?

这个问题的关键,不在于红黑树查得快不快。

而在于:

红黑树更适合内存查找B+树更适合以磁盘页为单位存储和读取的大规模数据

本文就从这个区别开始,把背后的原因一步步讲清楚。


一、先明确讨论范围:MySQL 不只有一种索引结构

严格来说,不能简单地说"MySQL 的所有索引都是 B+树"。

MySQL 支持多种存储引擎,不同索引类型也可能采用不同的数据结构。例如,MEMORY 存储引擎既支持 HASH 索引,也支持 BTREE 索引;空间索引还可能使用 R-tree。

本文讨论的是最常见的场景:

InnoDB 普通索引和聚簇索引为什么采用 B-tree 系列结构,而不是红黑树。

MySQL 官方文档通常统一称其为 B-tree 索引;在数据库原理的讲解中,由于其数据集中于叶子层、支持有序范围扫描等特点,通常会按照 B+树的思路进行理解。MySQL 官方文档也明确说明,B-tree 索引能够支持等值查询、范围查询、BETWEENIN 等操作。


二、红黑树是什么?

在理解为什么不用红黑树之前,先简单认识一下红黑树。

红黑树本质上是一种:

自动保持平衡的二叉搜索树。

"二叉"的意思是,每个节点最多只有两个子节点:

复制代码
左子树 < 当前节点 < 右子树

例如:

复制代码
             50
           /    \
         20      80
        /  \    /  \
      10   30  60   90

查找 60 时:

复制代码
50
↓
80
↓
60

只需要比较几次就能找到。

红黑树还会通过旋转、变色等操作,让整棵树不会严重倾斜,从而把查询、插入和删除的时间复杂度保持在较好的范围内。

所以红黑树本身并不慢。

如果数据全部放在内存中,红黑树是一种非常优秀的数据结构。

问题在于:

MySQL 的大量数据不能全部常驻内存,最终需要存储在磁盘或固态硬盘中。

这时,问题就发生了变化。


三、内存查找和磁盘查找,不是同一个问题

在内存中访问一个节点,速度很快。

但数据库中的数据量可能非常大:

复制代码
100 万行
1000 万行
1 亿行

这些数据不可能永远全部放在内存里。

当内存中没有需要的数据时,数据库需要从磁盘读取。

而磁盘读取的成本,远远高于在内存中进行一次大小比较。

因此,数据库索引真正关心的并不是:

一共比较了多少次?

而是:

为了找到数据,需要读取多少个磁盘页?

这就是理解红黑树与 B+树差异的核心。


四、数据库不是一个节点一个节点地读取数据

数据库从磁盘读取数据时,通常不是只读取几个字节。

它会按照"页"读取。

你可以把页理解为:

数据库与磁盘之间一次读取和写入的基本单位。

索引节点、索引记录和数据记录,会被组织到一个个页中。

MySQL 官方文档说明,InnoDB 的数据和索引存储在 B-tree 结构中,并以索引页的形式组织。

所以,数据库设计索引结构时要考虑:

复制代码
一个页能放多少索引项?
一次读取一个页后,能排除多少数据?
整棵树需要读取多少层?

这正是红黑树不占优势的地方。


五、红黑树的第一个问题:一个节点只有两个分支

红黑树属于二叉树。

每个节点最多只能分出两个方向:

复制代码
左边
右边

假设当前节点是 50:

复制代码
小于 50,往左
大于 50,往右

这意味着,每经过一层,最多只能把搜索范围大致缩小一半。

如果数据量不断增加,树的层数也会不断增加。

即使红黑树能够保持平衡,它仍然是一棵二叉树。

例如,一棵拥有大量数据的红黑树,可能需要经过很多层才能定位目标节点:

复制代码
根节点
↓
第2层
↓
第3层
↓
第4层
↓
......
↓
目标节点

如果这些节点分布在不同的磁盘页中,那么每向下一层,都可能需要读取一个新的页。

所以红黑树的问题不是查找复杂度差,而是:

分支太少,树容易变高;树越高,可能产生的页面访问就越多。


六、B+树的第一个优势:一个节点可以有很多分支

B+树不是二叉树,而是多路平衡查找树。

一个节点中可以存放多个索引键和多个子节点指针。

例如:

复制代码
              [20 | 40 | 60 | 80]
           /       |       |       |       \
        <20     20~40   40~60   60~80     >80

同样访问一层:

  • 红黑树只能判断"往左还是往右";

  • B+树可以直接判断目标属于多个区间中的哪一个。

如果一个节点能够管理几百个甚至更多子节点,那么一层就可以排除绝大部分数据。

因此,B+树即使存储非常多的数据,树的高度通常仍然比较低。

它的核心思路不是减少 CPU 比较次数,而是:

用更多分支降低树高,从而减少页读取次数。


七、为什么 B+树一个节点能放更多索引键?

B+树的非叶子节点主要负责导航。

可以简化理解为:

复制代码
索引键 + 子节点指针

非叶子节点不需要存放完整的数据行。

因此,在一个固定大小的页中,可以容纳更多索引键和子节点指针。

容纳的索引键越多:

复制代码
分支越多
↓
一层管理的数据越多
↓
树的高度越低
↓
查询需要访问的页越少

这就是 B+树设计中非常重要的"高扇出"特点。

这里的"扇出",就是一个节点能够拥有的子节点数量。

红黑树的扇出最多是 2。

B+树的扇出可以非常大。

这才是数据库选择 B+树而不是红黑树的核心原因之一。


八、用一个简化例子理解树高差异

假设有 100 万条数据。

为了方便理解,暂时忽略页大小、键长度、填充率等真实因素。

红黑树

因为它是二叉树,每个节点最多两个分支。

即使完全平衡,也需要很多层才能容纳 100 万个节点。

大致可以理解为:

复制代码
2^20 ≈ 100 万

也就是说,理想平衡的二叉树都可能需要接近 20 层。

红黑树不是严格意义上的完全平衡树,实际路径还可能更长。


B+树

假设一个非叶子节点能够分出 1000个分支。

那么:

复制代码
第一层:管理 1000 个方向
第二层:管理 1000 × 1000 个方向

两层分支理论上就已经可以覆盖约 100 万个范围。

真实数据库结构当然没有这么简单,还要考虑页容量、记录大小、填充率等因素,但这个例子足以说明:

多叉树通过提高每个节点的分支数量,可以显著降低树的高度。

所以,不能只看两种树的时间复杂度。

更应该看:

在磁盘页模型下,需要访问多少层。


九、红黑树的第二个问题:不能充分发挥"整页读取"的价值

红黑树的一个节点通常只包含:

复制代码
一个 key
左子节点指针
右子节点指针
其他平衡信息

如果把这种结构直接映射到磁盘存储中,一个节点能够提供的信息比较有限。

即使数据库一次从磁盘读取了一个完整页面,也很难像 B+树那样,在一个页中放入大量有序索引键,并一次性判断出目标属于哪个范围。

而 B+树天然适合页面结构:

复制代码
一个页面
├── 多个有序 key
├── 多个子节点指针
└── 页面管理信息

读取一个 B+树非叶子页,相当于一次拿到了一大批导航信息。

所以 B+树更能够利用数据库"一次读取一页"的特点。


十、B+树的第二个优势:天然支持范围查询

数据库不只会执行等值查询:

复制代码
SELECT * FROM user WHERE id = 100;

还会执行大量范围查询:

复制代码
SELECT * FROM user
WHERE id BETWEEN 100 AND 1000;

或者:

复制代码
SELECT * FROM orders
WHERE create_time >= '2026-01-01'
ORDER BY create_time;

MySQL 官方文档明确说明,B-tree 索引能够高效支持特定值、值集合和范围值的查询。

B+树的数据集中存放在叶子层,并按照索引键有序组织。

可以简化成:

复制代码
[1, 2, 3] → [4, 5, 6] → [7, 8, 9]

查询 3 到 8 时:

  1. 先通过树定位到 3;

  2. 从 3 开始顺序向后扫描;

  3. 扫描到 8 时结束。

这非常符合数据库范围查询的执行方式。


十一、红黑树做范围查询会怎样?

红黑树本身也能保存有序数据。

通过中序遍历,也可以按照顺序读取数据。

所以不能说红黑树完全不能做范围查询。

但问题在于,它的逻辑结构类似:

复制代码
             50
           /    \
         20      80
        /  \    /  \
      10   30  60   90

完成范围遍历时,需要不断处理:

  • 左子树;

  • 当前节点;

  • 右子树;

  • 父子节点之间的跳转。

这种结构更适合内存中的指针访问。

而 B+树把有序数据集中组织在叶子层,通过连续叶子页扫描完成范围读取,更符合数据库页和顺序扫描的需求。

因此,真正的区别不是:

复制代码
红黑树不能范围查询
B+树能范围查询

而是:

两者都能维护顺序,但 B+树的叶子层结构更适合数据库批量、连续地读取范围数据。


十二、B+树的第三个优势:等值、范围、排序能够统一处理

数据库索引不仅要支持:

复制代码
WHERE id = 100

还要支持:

复制代码
WHERE id > 100
WHERE id BETWEEN 100 AND 200
ORDER BY id

B+树中的索引键有序排列,因此可以同时服务于:

  • 等值查询;

  • 范围查询;

  • 有序扫描;

  • 部分排序和分组场景;

  • 最左前缀匹配。

MySQL 官方文档也将 B-tree 索引用于 =><=BETWEENIN 等条件。

红黑树也能维护顺序,但综合磁盘页访问、树高和连续扫描来看,B+树更适合作为通用数据库索引结构。


十三、那为什么 Java 的 TreeMap 可以使用红黑树?

看到这里,可能又会产生一个疑问:

既然 B+树这么好,为什么 Java 的 TreeMap 不用 B+树?

因为它们解决的问题不同。

TreeMap 的典型场景

TreeMap 中的数据通常位于内存。

访问一个节点,本质上是内存中的对象引用跳转。

它主要关心:

查询效率;

插入效率;

删除效率;

保持键有序;

动态平衡。

红黑树在这些场景下非常合适。


MySQL 索引的典型场景

MySQL 索引要管理的数据量可能远远超过内存容量。

它主要关心:

  • 如何减少磁盘页访问;

  • 如何让一层容纳更多索引项;

  • 如何降低整棵树的高度;

  • 如何高效进行范围扫描;

  • 如何把数据组织进固定大小的页。

因此:

复制代码
内存有序容器:红黑树很合适
磁盘数据库索引:B+树更合适

不是谁绝对更高级,而是应用场景不同。


十四、为什么不是普通 B树,而是 B+树?

讲到这里还可以再向前追问一步:

既然 B树也是多路平衡查找树,为什么还要改造成 B+树?

原因主要在于,B树的非叶子节点也可能存储真实数据。

这会带来两个影响。

第一,非叶子节点能够容纳的索引键减少

一个页中如果还要存放真实数据,那么留给索引键和子节点指针的空间就会减少。

索引键减少,分支数量就可能减少。

分支减少,树可能变高。


第二,数据分布在不同层级

B树中的数据可能出现在根节点、中间节点或叶子节点。

而 B+树把数据统一组织到叶子层,非叶子节点专门负责导航。

这让 B+树更适合:

  • 提高非叶子节点的扇出;

  • 降低树高;

  • 在叶子层进行连续范围扫描。

所以数据库索引真正看重的不是"能不能提前在中间节点找到数据",而是:

能不能让整个索引结构更矮,并让大量查询以更少、更连续的页面访问完成。


十五、B+树也不是没有代价

B+树并不是在所有场景下都优于红黑树。

它也有自己的成本。

例如:

  • 插入新记录时,可能需要进行页面分裂;

  • 删除数据后,可能出现页面利用率下降;

  • 随机插入可能导致索引页碎片;

  • 维护多棵索引会增加存储和写入成本。

MySQL 官方文档也指出,随机插入和删除二级索引记录可能造成索引碎片。

因此,B+树不是"没有缺点的数据结构"。

而是:

在磁盘页、范围查询、大数据量和通用数据库索引这些约束下,综合表现更合适。


十六、把整个选择过程串起来

现在可以把 MySQL 选择 B+树的逻辑串成一条完整路线:

复制代码
数据库数据量很大
↓
数据不能全部常驻内存
↓
查询可能需要读取磁盘页
↓
磁盘页访问成本很高
↓
需要尽量减少树的层数
↓
红黑树每个节点最多两个分支,树相对较高
↓
B+树一个节点可以拥有大量分支
↓
B+树树高更低,页面访问次数更少
↓
数据集中在有序叶子层
↓
范围查询可以连续扫描
↓
所以 InnoDB 更适合使用 B+树类型的索引结构

十七、面试怎么回答?

如果面试官问:

MySQL 为什么不用红黑树,而使用 B+树?

可以这样回答:

红黑树是一种平衡二叉搜索树,每个节点最多只有两个子节点。即使它能够保持平衡,在数据量很大时,树的高度仍然会比较高。

MySQL InnoDB 的数据和索引主要按照页进行存储和读取,查询时真正昂贵的是页面访问,而不是在内存中进行几次 key 比较。

B+树属于多路平衡查找树,一个非叶子节点可以存放多个索引键和子节点指针,因此分支更多、树高更低,能够减少查询过程中需要访问的页面数量。

同时,B+树的数据集中在叶子层,叶子记录按照索引键有序组织,非常适合范围查询和顺序扫描。

所以,红黑树更适合内存中的有序数据结构,而 B+树更适合大规模磁盘数据库索引。


十八、一个容易被忽略的纠正

很多资料在解释 B+树时,会强调:

B+树所有查询都走到叶子节点,所以查询更加稳定。

这句话不能算错,但它不是 MySQL 选择 B+树的最核心原因。

真正关键的原因应该按照这个优先级理解:

复制代码
高扇出
↓
树高低
↓
页面访问少
↓
叶子层有序
↓
范围扫描方便

至于"查询路径稳定",只能算结构上的一个特点,不应该当成决定性原因。

另外,也不能简单地说:

红黑树范围查询不行。

红黑树同样可以维护有序数据,也能通过遍历完成范围查询。

更加准确的说法是:

红黑树更适合内存中的节点跳转;B+树的叶子页结构更适合数据库按照页进行连续范围扫描。


十九、这一篇真正需要记住什么?

只需要记住四句话。

第一:

红黑树是二叉树,一个节点最多只有两个分支。

第二:

B+树是多叉树,一个节点可以拥有大量分支,因此树更矮。

第三:

数据库真正昂贵的是磁盘页访问,树越矮,通常需要访问的页越少。

第四:

B+树的叶子层有序组织,更适合范围查询和顺序扫描。

最终一句话总结:

红黑树解决的是内存中的有序查找问题,而 B+树解决的是大规模数据在磁盘页中的高效定位和范围扫描问题。


二十、结尾

学习数据库索引时,不能只背:

复制代码
MySQL 使用 B+树,因为 B+树查询快。

因为红黑树查询也很快。

真正需要理解的是:

不同数据结构的"快",是在不同存储模型下定义的。

在内存模型中,我们更关注算法复杂度和指针访问。

在数据库磁盘模型中,我们更关注:

复制代码
树有多高
需要读取多少页
每个页能提供多少导航信息
范围数据能不能连续读取

理解这一层以后,就不会再把红黑树、B树和 B+树简单地理解成"谁的时间复杂度更低"。

下一篇我们继续进入:

《MySQL 索引原理系列(三):MySQL 主键索引到底是什么?》

下一篇将正式讲清楚:

  • 主键是什么;

  • 主键索引是什么;

  • 聚簇索引是什么;

  • 为什么 InnoDB 聚簇索引中存放行数据;

  • 没有主键时,InnoDB 会怎么组织数据。

MySQL 官方文档说明,每张 InnoDB 表都会有一个用于存放行数据的聚簇索引;通常主键会被用作聚簇索引。如果没有主键,InnoDB 会依次寻找满足条件的唯一非空索引,或者生成隐藏的聚簇索引。

相关推荐
谙忆1 小时前
图片版权保护工程实战:明水印、盲水印(DCT隐写)、Referer防盗链与签名URL怎么落地
数据库
KaMeidebaby1 小时前
卡梅德生物技术快报|抗体合成:多肽抗体合成工程化方案:Nsp2 保守肽多抗制备与多维度验证
前端·网络·数据库·人工智能·算法
xfhuangfu2 小时前
19c CDB架构下的数据库启动和停止
数据库·架构
神道君天下2 小时前
填充与积累:积分与面积的可视化
数据库·oracle
爱折腾的小黑牛2 小时前
小程序云开发数据模型设计:礼账系统的三表结构
数据库·架构
爱折腾的小黑牛2 小时前
礼账小程序的数据安全方案:加密与备份
数据库·安全
JNX_SEMI2 小时前
AT2659S L1频段LNA:0.8dB噪声系数与20dB高增益设计解析
数据库
吠品4 小时前
PG_GBK 与 PG_UTF8 编码冲突:一次数据导入踩坑复盘
数据库
lld9510274 小时前
我做了一个 AI Agent Skill 云:用 MCP 在 Claude Code、Codex、OpenCode 之间同步技能
数据库·人工智能
ttwuai5 小时前
GoFrame 连接 MySQL 报错排查:parseTime、时区和 utf8mb4 怎么配
数据库·mysql·adb