深蓝黑背景品牌封面:左侧大标题「数据库索引为什么用 B+ 树」,副标题「树的高度决定磁盘 IO 次数」;右侧为巨大的半透明「B+」水印,下方是一棵三层 B+ 树装饰图——根节点扇出多个分支、底层叶子节点用虚线串联成链;顶部标注「经典面试题解析 · 数据库」与「Day 01 · 09-19」 图 1:本文封面,点明题目与三个关键词。

先说明属性:这是一篇经典题解析,题目出自本地历史面试资料(见文末来源说明),与任何公司的当前招聘无关。这道题几乎是数据库面试的开场题,但多数人停在「B+ 树矮、IO 少」这一句结论上,被追问「矮多少、少几次」就露怯了。

想象面试现场,面试官问:「数据库索引为什么用 B+ 树,用平衡二叉树不行吗?」

第一步,审题。

考察点不是背两种树的定义,而是能不能从存储介质出发做推导:索引存在磁盘上,读数据的单位是页,一次查询要读几页取决于树的高度。能自己推出「树高决定 IO 次数」,再算出两种结构的具体差距,这题才算答到位。

第二步,基础回答:先把磁盘这个前提摆上桌面。

内存里查找,平衡二叉树(如 AVL 树)和 B+ 树的比较次数都是 O(log n),差距不大。但数据库索引太大,主体在磁盘上。磁盘读写的特点是:一次 IO 读一整页,代价远大于内存比较;MySQL 官方文档写明 InnoDB 索引页的默认大小是 16KB。所以评价索引结构的第一指标不是比较次数,而是「从根到叶子要读几个页」——也就是树高。

平衡二叉树每个节点只存一个 key,分叉数是 2。一千万条记录,树高约 log2(10^7) ≈ 24 层,一次点查询最坏要读二十几个页。即使上层页能缓存进内存,靠近叶子的几层大概率要真正读盘。

B+ 树把每个节点做成一整页:内部页只存「key + 子页指针」,不存数据行。一页 16KB,假设一个 key 加指针占几十个字节,单页就能放下几百到上千个分叉。扇出按一千估算,一千万条记录的树高只要 3 层左右:一次点查询从根到叶子只需读 3 个页;其中根页和中间页通常常驻内存,真正落盘的磁盘 IO 往往只有一次。同样的数据量,24 层对 3 层,这就是结论的来源。

结构对比图:左侧平衡二叉树每个节点只存一个 key、扇出为 2,千万级记录树高约 24 层、每次查询约 24 次磁盘 IO;右侧 B+ 树一个节点就是一个磁盘页、可存数百至上千个 key,扇出约 1000 时千万级记录只要 3 层、3 次 IO,且叶子节点串成链表支持范围查询。底部结论:索引结构要矮胖不要高瘦——扇出越大树越矮,IO 越少;叶子成链,范围查询不用再爬树 图 2:同样的数据量,二叉树瘦高、B+ 树矮胖,层级差距就是 IO 差距。

第三步,B+ 树的另一半优势:叶子页的结构。

MySQL 官方文档说明,InnoDB 的索引记录都存在叶子页上,数据行则挂在聚簇索引(通常就是主键)的叶子页里。B+ 树的叶子页之间还有链表相连,范围查询(比如 BETWEEN、排序分页)找到一个起点后顺着链表顺序读就行;二叉树做范围扫描要在树里反复中序遍历,定位效率完全不在一个档次。文档还确认:二级索引的每条记录都带着主键值,用于回查聚簇索引——这解释了为什么主键要尽量短,也顺带解释了为什么页大小和扇出会反过来约束索引 key 的长度上限(这一点此处只提一句,不展开)。

第四步,可能的追问。

「为什么不用哈希索引?」等值查询哈希确实 O(1),但不支持范围、排序和最左前缀匹配,通用性输得彻底。「B 树和 B+ 树差在哪?」B 树的内部节点也存数据,扇出被压低、树变高,且范围扫描不如叶子链表顺。「树高 3 层是固定的吗?」不是,它由行数、行长和页大小共同决定,数据量再涨两个数量级树也只是从 3 层长到 4 层——扇出大,树高的增长极其缓慢。

工程应用与 AI 关联。

「固定页预算下,扇出决定层数,层数决定 IO」这条推理链是存储系统的通用语言。AI 应用里存 embedding 元数据、样本索引时面对的是同一道题;向量索引 IVF 用倒排列表控制一次查询扫描的分区数,HNSW 用分层图控制跳数,本质都是在「树高—IO 次数」这个坐标系上做权衡。看懂 B+ 树,再看这些结构会觉得似曾相识。

易错点清单。

一,只说「B+ 树更快」而不提磁盘页这个前提——内存场景下结论并不成立。二,把树高说成拍脑袋的数字,讲不出「页大小除以条目大小等于扇出」的推导。三,漏掉叶子链表,答不出范围查询为什么也受益。四,把哈希索引当成更优解,忽略范围与排序需求。

回到面试现场,建议的答题节奏:先立前提(索引在磁盘、读盘按页、页默认 16KB),再算两笔账(二叉树约 24 层、B+ 树约 3 层),然后补叶子链表对范围查询的支持,最后收在「评价指标是树高与 IO 次数」上。从介质特性推出结构选择,才是这题想看到的回答。

如果面试官继续追问「数据涨到百亿行,B+ 树还够用吗,下一步该怎么办」,你会从哪些方向延伸?欢迎写出你的思路。

参考资料

  • The Physical Structure of an InnoDB Index(MySQL 8.4 官方文档:InnoDB 索引为 B-tree 结构、索引记录存于叶子页、索引页默认 16KB、页大小由 innodb_page_size 在建实例时确定) — https://dev.mysql.com/doc/refman/8.4/en/innodb-physical-structure.html
  • Clustered and Secondary Indexes(MySQL 8.4 官方文档:聚簇索引即主键、二级索引记录包含主键列用于回查、主键越短二级索引越省空间) — https://dev.mysql.com/doc/refman/8.4/en/innodb-index-types.html