MySQL(InnoDB)的索引为什么用 B+ 树?

进阶高频原理约 3 分钟读完

一句话回答

数据存在磁盘上,查询快慢主要看磁盘 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 面试官对练,面试记录也会保存下来。登录

这道题你掌握了吗?

选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。

学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。