慢查询未必因缺索引,更常因索引建错或查询设计不当;需结合explain分析执行计划,关注type、key、rows,并依据选择性、查询频率、最左前缀等原则合理建索引。

慢查询真的只是因为没加索引吗
不一定。索引能改善的,是**基于条件字段的查找、排序、分组效率**;但如果慢查询源于 SELECT * 拉取大量字段、关联了 5 张以上大表、或 WHERE 条件里用了函数(如 WHERE YEAR(create_time) = 2023),加索引基本无效。
先用 EXPLAIN 看执行计划,重点关注:type 是否为 ALL 或 index(全表/全索引扫描)、key 是否为 NULL、rows 是否远超实际结果数。
哪些字段适合建索引
不是“所有 WHERE 字段都该加”,而是看**选择性**(distinct 值 / 总行数)和**查询频率**:
-
user_id、order_no这类高选择性字段,优先建单列索引 - 复合索引要遵循最左前缀原则:
INDEX (a, b, c)能加速WHERE a=1、WHERE a=1 AND b=2,但对WHERE b=2无效 - 避免给
TINYINT类型(如状态字段)单独建索引——区分度太低,优化效果微弱,反而拖慢写入 - 文本字段如
title,可考虑前缀索引:INDEX(title(50)),但需验证前 50 字符是否足够区分
ORDER BY 和 GROUP BY 怎么用索引加速
这两个操作能否走索引,取决于字段顺序和 ASC/DESC 是否匹配索引定义:
-
ORDER BY create_time DESC可用INDEX(create_time),但 MySQL 8.0 之前不支持混合方向,INDEX(a ASC, b DESC)在旧版本会退化为文件排序 -
GROUP BY user_id, status可用INDEX(user_id, status),但如果写成SELECT status, COUNT(*) FROM t GROUP BY status,而索引是(user_id, status),就无法覆盖,仍要临时表 - 带
LIMIT的分页慎用:ORDER BY id LIMIT 10000, 20仍要扫前 10020 行,建议改用游标式分页:WHERE id > last_seen_id ORDER BY id LIMIT 20
索引不是越多越好,这些坑很常见
每多一个索引,INSERT/UPDATE/DELETE 就要多维护一份 B+ 树,尤其在高频写入场景下,可能让性能更差:
- 重复索引:已有
INDEX(a, b),再建INDEX(a)是冗余的 - 隐式类型转换导致索引失效:比如
user_id是BIGINT,但查询写成WHERE user_id = '123'(字符串),MySQL 会转成函数调用,跳过索引 - 统计信息过期:
ANALYZE TABLE没定期跑,优化器可能选错执行计划,特别是大表数据分布变化后 - 覆盖索引被忽略:明明
INDEX(a, b, c)能覆盖SELECT a,b FROM t WHERE c=1,但若语句里有OR、!=、IS NULL等,也可能放弃使用
真正卡顿的慢查询,往往不是缺索引,而是索引建在了错误字段上、或者没配合查询模式设计。查 slow_query_log 时,别只盯着“没索引”,多看 Rows_examined 和 Rows_sent 的比值——差两个数量级,大概率是逻辑设计问题,不是索引能救的。











