索引是 MySQL 提升查询速度的核心手段,但用错反而会拖慢性能。我给你整理了最实用、最常用、能直接落地的索引优化方案,从原理到实战一步到位。
一、先搞懂:索引是什么?
简单说:索引 = 数据库的目录
- 没有索引:全表扫描(逐行找)
- 有索引:二分查找/树查找(快速定位)
- 代价:会占用磁盘空间,增删改变慢(因为要维护索引)
二、最核心的索引优化规则(必看)
1. 最左前缀原则(联合索引关键)
联合索引 (a, b, c) 生效规则:
- ✅
where a=?→ 生效 - ✅
where a=? and b=?→ 生效 - ✅
where a=? and b=? and c=?→ 生效 - ❌
where b=?→ 不生效 - ❌
where c=?→ 不生效 - ❌
where a=? and c=?→ 只用到 a
建联合索引口诀: 等值放前面,范围放最后;区分度高放前面
2. 不要在索引列上做运算/函数
索引一旦被计算,直接失效:
-- 坏:索引失效
where date(create_time) = '2025-01-01'
-- 好:索引生效
where create_time >= '2025-01-01 00:00:00'
and create_time < '2025-01-02 00:00:00'
3. 尽量使用覆盖索引(避免回表)
查询只查索引里有的字段,不用查原表:
-- 索引 (name, age)
select name,age from user where name='张三'
-- 直接从索引取数据,极快
4. 模糊查询 like 优化
- ✅
like '张%'→ 索引生效 - ❌
like '%张'/like '%张%'→ 索引失效
大量模糊搜索建议用:ES、Meilisearch
5. 避免 select *
永远只查需要的字段,减少回表、减少IO。
6. or 会导致索引失效
-- 坏:即使 name 有索引也会失效
where name='张三' or age=20
解决:拆成两条 SQL 或用 union
7. 区分度低的字段不要建索引
比如:性别、状态(0/1) 区分度 = 不重复值数量 / 总行数 区分度 < 20% 不建议建索引
8. 单表索引数量控制
建议:单表索引 ≤ 5 个 联合索引字段 ≤ 3 个
三、索引类型与最佳使用场景
| 索引类型 | 适用场景 |
|---|---|
| 主键索引 | 必须有,自增ID最好 |
| 唯一索引 | 手机号、身份证、订单号 |
| 普通索引 | 常用查询条件 |
| 联合索引 | 多条件组合查询(最常用) |
| 全文索引 | 文章、内容搜索(MySQL自带较弱) |
四、建索引黄金法则
- where 经常用到的字段
- order by / group by 字段
- join 关联字段
- 区分度高的字段优先
- 组合查询建联合索引
- 不常更新的表适合建索引
五、不建索引的场景
- 少量数据表(<1000行)
- 频繁增删改的表
- 区分度极低的字段
- 查询极少用到的字段
六、实战:如何检查索引是否生效?
用 explain 分析 SQL:
explain select * from user where name='张三';
重点看 2 个字段:
type:最优 → 最差system > const > eq_ref > ref > range > index > ALLALL = 全表扫描(必须优化)key:显示使用的索引名 → 为空=没用到索引
七、最常见的错误索引用法
- 单字段索引建太多
- 联合索引顺序乱建
- 索引列上使用函数
- 大量使用
%xxx% - 不加区分度盲目建索引
- 用
or连接多个索引字段
八、快速优化模板(直接套用)
假设表:user(id, name, age, city, create_time)
常用查询:
select name,age from user
where city=? and age>?
order by create_time desc;
最优索引:
create index idx_city_age_createtime on user(city, age, create_time);
总结
- 联合索引遵循最左前缀
- 索引列不运算、不函数
- 多用覆盖索引避免回表
- like %xxx 索引失效
- explain 检查索引是否生效
- 单表索引不超过5个