面试里常被问一句:InnoDB 一棵 B+ 树大概能存多少行? 常见口头答案是「大约两千万」。这个数字不是玄学,而是可以从页大小、主键宽度和指针长度推出来的。把推导路径走通,再回头看「为什么用 B+ 树而不是 B 树」会顺很多。
本文是对公开技术笔记的读者向整理,聚焦主键聚簇索引容量与树高;二级索引回表、填充因子等细节另文展开。
先对齐三个「最小单元」
磁盘、文件系统、InnoDB 各自有自己的粒度:
| 层级 | 常见最小单元 | 典型大小 |
|---|---|---|
| 磁盘 | 扇区 | 512B |
| 文件系统(XFS/EXT4 等) | 块 | 4KB |
| InnoDB | 页(Page) | 默认 16KB |
InnoDB 表空间里的 .ibd 文件大小通常是 16KB 的整数倍。可用:
SHOW VARIABLES LIKE 'innodb_page_size';
-- 默认 16384
数据行落在页里。若一行大约 1KB,一页大约能装 16 行(示意量级;真实页还要扣页头、槽位等开销)。
问题来了:只有「一页一页堆数据」时,怎么知道目标行在哪一页?全表扫页在大表上不现实。于是 InnoDB 用 B+ 树把页组织成索引组织表:叶子放行数据,非叶子放「键值 + 指向子页的指针」。
主键查找在树里怎么走
以主键查询为例:SELECT * FROM user WHERE id = 5。
- 找到该表主键索引的根页(表空间里有约定位置;教材与元数据里常见讨论会从 page number = 3 讲起)。
- 在根页上二分,按键区间落到某条指针。
- 再下到下一层页继续二分,直到叶子页拿到完整行。
要点:
- 叶子节点存完整记录(聚簇索引)。
- 非叶子节点只存键 + 指针,尽量提高扇出。
- 一次翻页 ≈ 一次磁盘 IO(再叠加缓冲池命中与否)。
所以树高直接决定主键点查的 IO 上界。
把「两千万」算出来
假设:
- 页大小 16KB = 16384 字节
- 行宽约 1KB → 叶子约 16 行/页
- 主键
BIGINT8 字节 + 子页指针 6 字节 ≈ 14 字节/项 - 非叶子一页可装指针数 ≈
16384 / 14 ≈ 1170
树高 2(根 + 一层叶子):
1170 × 16 ≈ 18,720
约 1.8 万行量级。
树高 3(根 + 一层分支 + 叶子):
1170 × 1170 × 16 ≈ 21,902,400
约 2190 万行——这就是「大概两千万」的来源。
因此 InnoDB 主键 B+ 树高度常见在 1~3:千万级表仍经常是 3 层,主键点查大约 1~3 次页 IO(缓存未命中时)。
树高怎么和 page level 对上
教材里常见写法:根页固定偏移处存 page level,树高 ≈ page level + 1。
page level = 0 → 高度 1;page level = 2 → 高度 3。
可用元数据确认各索引的 PAGE_NO,再用 hexdump 等工具读表空间字节做验证(生产环境务必只读副本,别在主库上乱玩文件)。
实践里也常见:行数从十几万到六百万的表,主键高度都可能是 3——因为从「高度 2 的容量上限」跨到「高度 3」之后,还有很大一段空间。行数差一个数量级,点查 IO 次数未必差很多,这是 B+ 树扇出大的直接收益。
为什么是 B+ 树,而不是普通 B 树?
把真实数据塞进非叶子节点会怎样?
- 非叶子每页能装的指针变少(扇出下降)
- 同样数据量下树更高
- 查找路径上的 IO 变多
B+ 树把数据压到叶子、叶子之间再链式串联,还更适合范围扫描。所以 MySQL/InnoDB 索引默认站在 B+ 这一侧,不是「B 树不能用」,而是在磁盘 IO 模型下 B+ 更划算。
顺带:最左前缀
联合索引 (name, age, sex) 按从左到右比较。只有 name 条件时索引可用;只有 age、没有前导 name 时,优化器通常用不上这棵联合索引树。建索引和写 WHERE 时把「最左前缀」当成约束,比背一堆口诀稳。
索引到底在优化什么
索引把「随机找」收成「沿树收窄范围」,再配合操作系统预读:一次 IO 会带上相邻页,页内再二分。磁盘一次 IO 往往是毫秒级,而 CPU 指令便宜几个数量级——所以数据结构的目标很朴素:把单次查询的磁盘 IO 压到常数级(通常个位数页)。
B+ 树高度公式直觉:h ≈ log_m(N),其中 m 是每页能装多少索引项。页固定时,键越短、非叶子越不夹带行数据,m 越大,h 越低。这也是为什么主键倾向用紧凑整数、而不是超长字符串当聚簇键。
小结(可直接当面试答法骨架)
- InnoDB 以 16KB 页组织数据;主键是聚簇 B+ 树。
- 非叶子高扇出(约千级指针/页)× 叶子约十几行/页 → 高度 3 可到约两千万行量级(行宽 1KB 假设下)。
- 主键点查 IO 上界与树高绑定,常见 1~3。
- 用 B+ 而不是把数据塞满内节点的 B 树,是为了更高扇出、更矮树、更少 IO。
- 联合索引记得最左前缀。
数字会随行宽、页填充、主键类型变化;重要的是会推,而不是背死「两千万」四个字。
参考
- 姜承尧《MySQL 技术内幕:InnoDB 存储引擎》及公开笔记中的 page level 验证思路
- 公开技术文整理:InnoDB B+ 树容量推算、索引与磁盘 IO 讨论
- 笔记源:
zhao-note→30-技术/数据与中间件/MySQL/MySQL-B+树与索引原理.md(资料体,含图示摘录;本文为读者向改写)




