Skip to content

MySQL 索引原理

MySQL 索引是理解数据库查询性能的核心。重点包括 B+Tree、聚簇索引、二级索引、回表、覆盖索引、最左前缀、索引下推、索引失效和 EXPLAIN


1. 索引是什么

索引是为了加速查询而维护的数据结构。

没有索引时:

text
全表扫描

有索引时:

text
通过索引快速定位数据

索引不是越多越好。索引会提升查询性能,但会增加写入、更新、删除成本,也会占用磁盘空间。


2. 为什么 InnoDB 使用 B+Tree

B+Tree 特点:

  • 多叉树,高度低。
  • 非叶子节点只存 key 和指针。
  • 叶子节点存储完整数据或主键值。
  • 叶子节点之间有链表,适合范围查询。

相比二叉树,B+Tree 高度更低,磁盘 IO 更少。

相比 Hash 索引,B+Tree 支持范围查询和排序。


3. 聚簇索引

InnoDB 表数据本身就是按主键组织的一棵 B+Tree。

聚簇索引叶子节点存放完整行数据:

text
主键 -> 完整行记录

所以按主键查询通常很快。

如果表没有显式主键,InnoDB 会选择一个唯一非空索引;如果也没有,会生成隐藏 row id。


4. 二级索引

二级索引也叫辅助索引。

二级索引叶子节点存放:

text
索引列值 -> 主键值

如果查询字段不在二级索引中,需要根据主键再查一次聚簇索引,这个过程叫回表。


5. 回表

示例:

sql
select name, age from user where phone = '13800000000';

如果 phone 上有索引,但索引中没有 nameage,流程是:

text
通过 phone 索引找到主键 id
  -> 通过 id 回到聚簇索引
  -> 读取完整行

回表次数太多会影响性能。


6. 覆盖索引

如果查询需要的字段都在索引中,就不需要回表。

例如联合索引:

sql
index idx_phone_name(phone, name)

查询:

sql
select phone, name from user where phone = '13800000000';

只查索引就能返回结果,这就是覆盖索引。


7. 最左前缀原则

联合索引:

sql
index idx_a_b_c(a, b, c)

可以命中:

sql
where a = ?
where a = ? and b = ?
where a = ? and b = ? and c = ?

不能很好命中:

sql
where b = ?
where c = ?
where b = ? and c = ?

因为联合索引按从左到右排序,查询必须从最左列开始使用。


8. 范围查询后的列

联合索引中,如果某列使用范围查询,后续列通常不能继续用于精确定位。

例如:

sql
where a = ? and b > ? and c = ?

索引可以有效使用 ab,但 c 很难继续用于缩小扫描范围。


9. 索引下推

索引下推(Index Condition Pushdown)是 MySQL 的优化。

没有索引下推时,存储引擎通过索引找到记录后,需要回表交给 Server 层判断其他条件。

有索引下推时,能在存储引擎层先用索引中的字段过滤一部分数据,减少回表。


10. 索引失效场景

常见场景:

  • 对索引列使用函数。
  • 对索引列进行计算。
  • 隐式类型转换。
  • 联合索引不满足最左前缀。
  • 前置 %like
  • or 两边不是都有索引。
  • 数据区分度太低,优化器认为全表扫描更划算。

示例:

sql
where date(create_time) = '2026-06-22'

对索引列使用函数,可能导致索引失效。更好的写法是:

sql
where create_time >= '2026-06-22 00:00:00'
  and create_time <  '2026-06-23 00:00:00'

11. EXPLAIN 重点字段

字段含义
type访问类型
possible_keys可能使用的索引
key实际使用的索引
rows预计扫描行数
filtered条件过滤比例
Extra额外信息

type 常见性能从好到差:

text
system -> const -> eq_ref -> ref -> range -> index -> ALL

看到 ALL 要警惕全表扫描。


12. 理解检查

为什么建议主键递增?

递增主键插入更接近顺序写,减少页分裂和随机 IO。UUID 作为主键可能导致索引页频繁分裂。

什么是回表?

通过二级索引找到主键后,再通过主键查询聚簇索引获取完整行。

什么是覆盖索引?

查询所需字段都在索引中,无需回表。

索引是不是越多越好?

不是。索引会增加写入成本和存储成本,也会影响优化器选择。


13. 总结

MySQL 索引的核心是 B+Tree。聚簇索引存完整行,二级索引存主键,回表影响性能,覆盖索引能减少回表。联合索引设计要遵守最左前缀,并结合查询条件和区分度。

Released under the MIT License.