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+ 树?
- 磁盘友好:树高通常只有 3-4 层,查询最多只需 3-4 次磁盘 I/O
- 范围查询快:叶子节点的链表让范围查询只需遍历链表
- 插入删除稳定:自平衡机制保证操作复杂度稳定在 O(log n)
Hash 索引
Memory 引擎的默认索引类型,InnoDB 也支持自适应 Hash 索引。
// 想象一下 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 树是个好选择。
全文索引
用于文本搜索,比如在文章中查找包含某个关键词的记录。
-- 创建全文索引
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 │
└─────────┴────────┘查询流程:先在索引树找到叶子节点获取主键,再用主键去聚簇索引查完整数据——这就是「回表」。
按字段数量分类
单列索引
索引只包含一个列,最基础的索引类型。
CREATE INDEX idx_user_name ON users(name);联合索引(多列索引)
索引包含多个列,按创建顺序排列。
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 树索引 |
面试追问方向
- 聚簇索引和非聚簇索引的区别?各自的优势和劣势是什么?
- 什么情况下加了索引但查询还是很慢?
索引不是万能药。索引失效的情况、慢查询的排查方法,我们后续章节会详细讲解。
