Skip to content

MySQL 索引类型:一文搞懂所有索引分类

凌晨 3 点,你被生产环境的慢查询告警吵醒。

SELECT * FROM orders WHERE status = 'pending' AND create_time > '2024-01-01';

这条 SQL 跑了整整 30 秒。 DBA 看了一眼,轻飘飘说了句:「加个索引吧。」

你加了索引,查询瞬间降到 50 毫秒。

但问题是:MySQL 有多少种索引类型?各自适用于什么场景?什么时候该用,什么时候不该用?

今天,我们来彻底搞懂 MySQL 的索引体系。


索引的分类维度

MySQL 的索引可以从多个维度来分类:

分类维度类型说明
数据结构B+ 树索引、Hash 索引、R 树索引、全文索引最常见的分类方式
存储方式聚簇索引、非聚簇索引决定数据的物理存储顺序
字段数量单列索引、联合索引按索引包含的字段数
唯一性主键索引、唯一索引、普通索引按索引的唯一性约束

按数据结构分类

B+ 树索引

这是 MySQL(InnoDB 引擎)默认的索引类型,几乎 99% 的场景都在用它。

B+ 树是一种多路平衡查找树,所有数据都存储在叶子节点,叶子节点之间用双向链表连接。

为什么 MySQL 选 B+ 树?

  1. 磁盘友好:树高通常只有 3-4 层,查询最多只需 3-4 次磁盘 I/O
  2. 范围查询快:叶子节点的链表让范围查询只需遍历链表
  3. 插入删除稳定:自平衡机制保证操作复杂度稳定在 O(log n)

Hash 索引

Memory 引擎的默认索引类型,InnoDB 也支持自适应 Hash 索引。

java
// 想象一下 Hash 索引的查询过程
public Object hashSearch(Map<String, Object> hashIndex, String key) {
    // 直接通过 Hash 函数计算地址,O(1) 时间复杂度
    int hashCode = key.hashCode();
    int index = hashCode % hashIndex.capacity();
    return hashIndex.get(index);
}

优点:单条查询极快,等值查询的复杂度是 O(1)。

缺点

  • 不支持范围查询(Hash 无序)
  • 不支持排序
  • 不支持最左前缀匹配
  • 存在 Hash 冲突

适用场景:等值查询极多的业务表,比如 Session 表、配置表。

R 树索引

空间索引的一种,用于地理空间数据类型(GEOMETRY、POINT 等)。

如果你要做「附近的人」这种功能,R 树是个好选择。

全文索引

用于文本搜索,比如在文章中查找包含某个关键词的记录。

sql
-- 创建全文索引
ALTER TABLE articles ADD FULLTEXT INDEX ft_title_content (title, content);

-- 使用全文索引查询
SELECT * FROM articles WHERE MATCH(title, content) AGAINST('MySQL 优化');

但说实话,生产环境做全文搜索,一般都用 Elasticsearch,MySQL 的全文索引只适合小数据量场景。


按存储方式分类

聚簇索引(Clustered Index)

每个表只能有一个聚簇索引,数据行的物理存储顺序与索引顺序一致。

InnoDB 中,聚簇索引就是主键索引。如果没有主键,会选择一个唯一非空索引;如果也没有,InnoDB 会生成一个隐藏的行 ID 作为聚簇索引。

聚簇索引的叶子节点直接存储完整的数据行:

聚簇索引结构:
┌─────────┬────────────────────────────────────────┐
│ 索引键  │ 叶子节点                               │
├─────────┼────────────────────────────────────────┤
│ 1       │ [id=1, name='张三', age=25, ... 整行] │
│ 2       │ [id=2, name='李四', age=30, ... 整行] │
│ 3       │ [id=3, name='王五', age=28, ... 整行] │
└─────────┴────────────────────────────────────────┘

这意味着:主键查询可以直接返回数据,无需回表。

非聚簇索引(Secondary Index)

也叫二级索引、辅助索引。除了聚簇索引外的所有索引都是非聚簇索引。

非聚簇索引的叶子节点存储的是索引键和主键值:

非聚簇索引结构(name 列上的索引):
┌─────────┬────────┐
│ 索引键  │ 主键值 │
├─────────┼────────┤
│ '张三'  │ 1      │
│ '李四'  │ 2      │
│ '王五'  │ 3      │
└─────────┴────────┘

查询流程:先在索引树找到叶子节点获取主键,再用主键去聚簇索引查完整数据——这就是「回表」。


按字段数量分类

单列索引

索引只包含一个列,最基础的索引类型。

sql
CREATE INDEX idx_user_name ON users(name);

联合索引(多列索引)

索引包含多个列,按创建顺序排列。

sql
CREATE INDEX idx_user_status_time ON users(status, create_time);

联合索引有最左前缀原则:只有从最左边的列开始使用,索引才能生效。

比如 idx_user_status_time 可以生效的查询:

  • WHERE status = 'active'
  • WHERE status = 'active' AND create_time > '2024-01-01'

不能生效的查询:

  • WHERE create_time > '2024-01-01'(跳过最左列)
  • WHERE name = '张三' AND status = 'active'(顺序不对)

按唯一性分类

主键索引

  • 唯一且非空
  • 每个表只能有一个
  • InnoDB 中即聚簇索引

唯一索引

  • 值唯一(可以为 null)
  • 可以有多个
  • 不一定是聚簇索引

普通索引

没有任何唯一性约束,仅用于加速查询。


索引选择建议

场景推荐索引类型
主键查询主键索引(聚簇索引,无回表)
等值查询单列索引
范围查询 + 等值查询联合索引,注意最左前缀
多条件组合查询联合索引,合理安排列顺序
文本搜索全文索引(小数据量)或 ES(大数据量)
地理位置查询R 树索引

面试追问方向

  • 聚簇索引和非聚簇索引的区别?各自的优势和劣势是什么?
  • 什么情况下加了索引但查询还是很慢?

索引不是万能药。索引失效的情况、慢查询的排查方法,我们后续章节会详细讲解。

基于 VitePress 构建