No.208
MySQL(InnoDB)的索引为什么用 B+ 树?
一句话回答
数据存在磁盘上,查询快慢主要看磁盘 I/O 次数。B+ 树是多路平衡树,一个节点就是一页(默认 16KB),能放上千个键,所以树很矮:两千万行左右的表通常 3 层就够,一次查询只要 3 次左右的 I/O。而且数据都在叶子节点,叶子节点之间用链表相连,范围查询和排序也很高效。
详细解析
和其他数据结构对比
| 结构 | 问题 |
|---|---|
| 哈希表 | 等值查询快,但不支持范围查询和排序 |
| 二叉搜索树 / 红黑树 | 每个节点只有两个分支,数据量大时树很高,I/O 次数多;普通二叉搜索树还可能退化成链表 |
| B 树 | 非叶子节点也存数据,每页能放的键更少,树更高;范围查询要在树中来回遍历 |
| B+ 树 | 非叶子节点只存键和指针,分支多、树矮;数据都在叶子节点,叶子之间有链表,范围查询顺序扫描即可 |
估算:3 层 B+ 树能存多少行
假设主键是 BIGINT(8 字节),页内指针 6 字节,一页 16KB:
- 一个非叶子节点约能存 16384 ÷ (8 + 6) ≈ 1170 个指针
- 假设一行数据 1KB,一个叶子节点能存 16 行
- 3 层能存 1170 × 1170 × 16 ≈ 2190 万行
聚簇索引和二级索引
- 聚簇索引(主键索引):叶子节点存整行数据,一张表只有一个
- 二级索引(普通索引):叶子节点存索引列和主键值。查到主键后还要回到聚簇索引查整行,这叫回表
- 覆盖索引:要查询的列都在二级索引里,就不用回表。比如在
name上建了索引,SELECT id, name FROM user WHERE name = 'Tom'就不需要回表
面试官可能追问
为什么推荐用自增 ID 做主键?
自增 ID 让新数据总是追加在最后一页,不会引起页分裂和数据移动。UUID 这类随机主键插入位置不确定,会频繁页分裂、产生碎片;而且主键越长,所有二级索引也越大,因为二级索引的叶子节点要存主键值。
什么是最左前缀原则?
联合索引 (a, b, c) 先按 a 排序,a 相同再按 b,b 相同再按 c。查询条件必须从最左边的列开始连续使用,才能用索引定位:WHERE a = 1 AND b = 2 可以,WHERE b = 2 不行。遇到范围条件(如 a > 1)后,后面的列就不能再用来定位了。
哪些情况下索引会失效?
- 对索引列做函数运算或计算,如
WHERE YEAR(create_time) = 2024 - 隐式类型转换,比如字符串类型的列用数字去比较
LIKE '%abc'这种左模糊匹配- 不满足最左前缀原则
OR的两边有一边没有索引- 优化器判断全表扫描比走索引更快
易错点
- InnoDB 的页默认是 16KB,不是 4KB
- MyISAM 的索引是非聚簇的,叶子节点存的是数据行的地址,而不是整行数据
AI 模拟面试官
用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮
登录后就可以和 AI 面试官对练,面试记录也会保存下来。登录
这道题你掌握了吗?
选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。
学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。