<p style="text-align:center"><img src="https://platform-outputs.agnes-ai.space/images/t2i/task_6GGTQOoMdOQY47vutbDAYHMMCWrposRw/output_4a29ec5b76ea4259a34c434ac21b19a7.png" alt="封面图" style="max-width:100%;height:auto" /></p>
MySQL 索引优化避坑指南没效果?EXPLAIN 没搞透
干了十年运维,见过太多索引建了等于没建的案例。开发兄弟自信满满说加了索引,线上查询还是慢如蜗牛,最后背锅的往往是 DBA 或运维。其实问题多半出在没看懂 EXPLAIN 执行计划,盲目优化。别光盯着 key 列看有没有索引,type 和 extra 才是照妖镜。今天咱们不聊虚的,直接上干货,拆解三个生产环境最高频的索引失效坑,帮你彻底搞定索引失效难题,生产环境不再背锅。
核心字段看这里
执行 `EXPLAIN SELECT ...` 后,重点关注这三列。type 代表访问类型,至少要是 `range` 级别,如果是 `ALL` 就是全表扫描,必须优化。const 和 ref 是理想状态,表示精确匹配。很多新人只看 key 有没有值,其实 type 更能反映性能瓶颈。key 列显示实际使用的索引,如果为 NULL,说明索引没用上。Extra 列包含额外信息,出现 `Using filesort` 或 `Using temporary` 警铃大作,说明需要文件或临时表排序,性能损耗大。理解这些底层原理,才能对症下药。
坑一:索引列上搞计算
很多兄弟喜欢这么写查询,觉得方便,这种写法在代码审查里经常被放过,但上线就是故障:
EXPLAIN SELECT * FROM users WHERE YEAR(create_time) = 2023;**输出效果**:type 显示 `ALL`,key 为 `NULL`,扫描行数全表。
**原因**:在索引列 `create_time` 上用了 `YEAR()` 函数,导致索引失效。MySQL 无法直接使用索引树匹配计算后的值,只能逐行计算再比较。
**修正**:改为范围查询 `WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'`,type 立刻变 `range`,效率提升百倍。
坑二:联合索引违背最左前缀
建立了联合索引 `idx_abc (a, b, c)`,查询却这样写,以为能命中,尤其是动态 SQL 拼接时,容易漏掉第一个字段:
EXPLAIN SELECT * FROM orders WHERE b = 100 AND c = 200;**输出效果**:key 为 `NULL`,type 为 `ALL`,完全没走索引。
**原因**:联合索引遵循最左前缀原则。索引树是先按 a 排序,再按 b 排序。跳过字段 `a` 直接查 `b`,索引树无法定位,相当于没索引。
**修正**:查询条件必须包含 `a`,如 `WHERE a = 1 AND b = 100`,才能命中索引,type 变为 `ref`。
坑三:模糊查询百分号在前
搜索功能最容易踩的坑,业务方要求模糊匹配,数据量少时感觉不明显,千万级数据直接拖垮数据库:
EXPLAIN SELECT * FROM products WHERE name LIKE '%手机%';**输出效果**:type 为 `ALL`,Extra 显示 `Using where`,全表扫描。
**原因**: `%` 在前缀,索引树的有序性被破坏,B+ 树无法利用索引进行范围查找,只能全表扫描匹配。
**修正**:尽量改为 `LIKE '手机%'`,走索引范围扫描。或者使用 Elasticsearch 处理复杂搜索,别硬扛 MySQL。
老运维的避坑清单
1. 索引列严禁做计算、函数操作,保持列纯净。
2. 联合索引严格遵循最左前缀匹配,顺序不能乱。
3. 模糊查询 `%` 不能放在开头,否则索引失效。
4. 类型隐式转换会导致索引失效,如字符串不加引号。
5. 定期清理未使用的冗余索引,维护成本也是成本。这条清单建议收藏,每次写 SQL 前对照一遍。
索引优化不是玄学,是科学。EXPLAIN 结果里的 Extra 字段你还遇到过哪些警告?