MySQL 索引类型:从基础到优化,一篇讲透
面试官:"MySQL 有哪些索引类型?"
你:"主键索引、唯一索引、普通索引、复合索引、全文索引。索引能大大加快查询速度,但会降低增删改的性能。"
面试官:"那复合索引的最左前缀原则是什么?为什么会有这个原则?"
你:"......"
很多人能列出索引类型,但一追问"什么时候索引会失效""如何设计高效索引"就含糊了。本文从索引的底层结构出发,讲透各种索引的特点和使用场景。
一、索引是什么?
索引是数据库中的一种数据结构 ,类似于书的目录,可以快速定位数据所在的位置,避免全表扫描。MySQL 中常用的索引结构是 B+Tree(InnoDB 存储引擎)。索引虽然能加速查询,但需要额外的存储空间,并且在数据插入、更新、删除时需要同步维护,因此会降低写入性能。
二、五种索引类型详解
1. 主键索引(PRIMARY KEY)
- 特点:唯一且非空,一个表只能有一个主键索引。
- 底层:聚簇索引(Clustered Index),叶子节点直接存储整行数据。
- 创建:
sql
CREATE TABLE user (
id INT PRIMARY KEY, -- 主键索引
name VARCHAR(50)
);
-- 或单独添加
ALTER TABLE user ADD PRIMARY KEY (id);
- 注意:InnoDB 中,如果没有显式定义主键,会自动选择一个唯一非空索引作为聚簇索引;如果也没有,则隐式生成一个 6 字节的 rowid 作为主键。建议每个表都显式指定主键。
2. 唯一索引(UNIQUE)
- 特点:索引列的值必须唯一,但允许 NULL(可以有多个 NULL)。
- 作用:保证数据唯一性,同时加速查询。
- 创建:
sql
CREATE TABLE user (
id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE -- 唯一索引
);
-- 或
CREATE UNIQUE INDEX idx_email ON user(email);
3. 普通索引(INDEX / KEY)
- 特点:最基本索引,无唯一性约束,允许重复值。
- 作用:加速查询,是最常用的索引类型。
- 创建:
sql
CREATE INDEX idx_name ON user(name);
4. 复合索引(Composite Index)
- 特点 :在多个列上建立一个索引,遵循最左前缀原则。
- 作用 :同时加速对多个列的查询,尤其适合
WHERE条件中包含多个字段的场景。 - 创建:
sql
CREATE INDEX idx_name_age ON user(name, age);
查询 WHERE name = '张三' AND age = 25 可以利用该索引;但 WHERE age = 25 不能使用该索引(因为没有使用索引的最左列 name)。
5. 全文索引(FULLTEXT)
- 特点 :用于全文搜索(类似搜索引擎),仅支持
MyISAM和InnoDB(MySQL 5.6+)。 - 作用 :对文本内容进行关键词匹配,比
LIKE '%keyword%'高效得多。 - 创建:
sql
CREATE TABLE article (
id INT PRIMARY KEY,
content TEXT,
FULLTEXT(content)
);
-- 查询
SELECT * FROM article WHERE MATCH(content) AGAINST('数据库优化');
- 注意:全文索引有停用词、最小词长度等限制,适合大文本字段的搜索场景。
三、索引的优缺点
| 优点 | 缺点 |
|---|---|
大大加快 SELECT 查询速度 |
占用额外磁盘空间 |
加速 ORDER BY、GROUP BY |
降低 INSERT、UPDATE、DELETE 速度(需维护索引) |
| 唯一索引能保证数据唯一性 | 索引设计不合理会导致查询优化器选错索引 |
权衡:索引不是越多越好。一般建议对查询频繁、区分度高的列建索引,避免在低基数列(如性别)或频繁更新的列上建索引。
四、复合索引的最左前缀原则(重点)
复合索引 (a, b, c) 实际上相当于创建了三个索引:(a)、(a, b)、(a, b, c)。查询时只有从索引的最左列开始匹配才能使用索引。
能用索引的情况:
WHERE a = 1WHERE a = 1 AND b = 2WHERE a = 1 AND b = 2 AND c = 3WHERE a = 1 AND c = 3(只用到 a,c 无法使用,但 a 可过滤一部分)
不能用索引的情况:
WHERE b = 2(没有最左列 a)WHERE b = 2 AND c = 3
范围查询的影响:
WHERE a = 1 AND b > 2 AND c = 3:a 和 b 能用到索引,c 用不到(因为 b 是范围查询,后面的列停止匹配)。
设计建议:将区分度高的列放在复合索引左侧,等值查询的列优先于范围查询的列。
五、索引失效的常见场景
| 场景 | 示例 | 原因 |
|---|---|---|
| 对索引列使用函数 | WHERE YEAR(create_time) = 2024 |
无法使用索引,应改为范围查询 |
| 隐式类型转换 | WHERE phone = 13800138000(phone 是 varchar) |
类型不匹配,索引失效 |
| LIKE 以通配符开头 | WHERE name LIKE '%张三' |
无法使用索引 |
| OR 前后未全索引 | WHERE a = 1 OR b = 2(只有 a 有索引) |
需要全表扫描 |
使用 != 或 <> |
WHERE status != 0 |
非等值查询一般不用索引 |
使用 IS NULL 或 IS NOT NULL |
某些情况下失效,取决于版本和数据分布 | - |
六、如何评估索引是否有效?
使用 EXPLAIN 命令查看执行计划:
sql
EXPLAIN SELECT * FROM user WHERE name = '张三';
关键列:
- type :
const>ref>range>index>ALL(好到差) - possible_keys:可能使用的索引
- key:实际使用的索引
- rows:预估扫描行数
- Extra :
Using index表示覆盖索引(不回表),Using where表示需要过滤
七、常见面试追问
Q1:主键索引和唯一索引的区别?
- 主键索引不允许 NULL,唯一索引允许 NULL(多个 NULL)。
- 一个表只能有一个主键,可以有多个唯一索引。
- 主键通常是聚簇索引(InnoDB),唯一索引是辅助索引。
Q2:为什么推荐使用自增整数做主键?
- 插入时顺序写入,B+Tree 页分裂少,性能高。
- UUID 或随机字符串作为主键会导致随机插入,页分裂频繁,且占用空间大。
Q3:什么是覆盖索引?
如果一个索引包含了查询所需的所有列(即 SELECT 的列都在索引中),那么不需要回表查询数据行,称为覆盖索引。例如:
sql
CREATE INDEX idx_name ON user(name);
SELECT name FROM user WHERE name = '张三'; -- 覆盖索引,不回表
Q4:索引下推(Index Condition Pushdown)是什么?
MySQL 5.6 引入的优化:在索引遍历过程中,对索引中包含的字段先做条件过滤,减少回表次数。例如 INDEX(a, b),查询 WHERE a = 1 AND b = 2,没有 ICP 时会先根据 a=1 回表再过滤 b;有 ICP 时直接在索引中判断 b=2,只回表符合的数据。
Q5:联合索引中,字段顺序如何设计?
- 区分度高的列放在左侧。
- 等值查询的列放在左侧,范围查询的列放在右侧。
- 经常用于排序的列可以考虑加入索引(
ORDER BY也能利用索引顺序)。
八、总结
| 索引类型 | 特点 | 适用场景 |
|---|---|---|
| 主键索引 | 唯一、非空、聚簇 | 每张表必备,作为行标识 |
| 唯一索引 | 值唯一,可 NULL | 保证字段唯一性(如邮箱、手机号) |
| 普通索引 | 无约束 | 加速查询,最常用 |
| 复合索引 | 多列组合,最左前缀 | 多条件查询、排序 |
| 全文索引 | 关键词匹配 | 大文本搜索(如文章、评论) |
一句话记住索引设计 :等值左前缀,范围右靠后;覆盖索引少回表,函数运算全失效。
索引是把双刃剑,合理的索引能让查询飞起来,滥用索引会让写入慢如牛。理解索引类型和原理,是 MySQL 优化的第一步。
希望这篇文章能帮你彻底掌握 MySQL 索引的相关知识,从容应对面试和实际调优,欢迎继续讨论。
我的个人简介最后有一段内容,感兴趣的朋友可以去找找看。那里有我日常分享的技术深度 解析和职场避坑指南,期待与您继续交流。