90%的慢查询问题源于where条件写法不当和索引未被使用,应先用explain分析执行计划,重点关注type(all表示全表扫描)、key(null说明未用索引)、rows(接近总行数即失效),再优化sql而非盲目加索引。

直接结论:90% 的慢查询问题,不是数据库配置或硬件瓶颈,而是 WHERE 条件写法不当 + 索引没用上。先看执行计划,再动 SQL,最后才考虑加索引。
用 EXPLAIN 看清 MySQL 实际怎么查
别猜,直接执行 EXPLAIN 查看优化器真实选择。重点盯三个字段:type、key、rows。
-
type = ALL表示全表扫描,必须处理;type = range或ref才算走索引 -
key为空,说明没用上任何索引;哪怕possible_keys有值,key是空的也白搭 -
rows值接近表总行数(比如 500 万行查出rows=4982341),基本等于没走索引 - MySQL 8.0+ 推荐用
EXPLAIN ANALYZE,它会真跑一遍并返回实际扫描行数,比预估更准
WHERE 条件里最容易踩的坑
很多 SQL 看起来合理,但一写就让索引失效。常见触发点:
-
WHERE DATE(create_time) = '2026-06-01'→ 函数包裹索引列,强制全表扫。应改写为WHERE create_time >= '2026-06-01' AND create_time -
WHERE status != 'done'或WHERE status NOT IN ('done', 'cancel')→ 非等值判断通常不走索引,尤其当结果集大时 -
WHERE name LIKE '%张%'→ 左模糊,B+ 树索引无法利用。若必须模糊查,考虑全文索引或前置生成冗余字段 -
WHERE user_id = 123 OR order_no = 'NO20260601'→ 只要其中一个字段没索引,整个 OR 条件大概率退化为全表扫描。改用UNION ALL拆开,各自走索引
JOIN 和 SELECT * 是性能隐形杀手
看似无害的操作,在数据量上来后会迅速暴露问题:
-
SELECT *不仅传输更多数据,还容易导致“回表”——即使走了索引,也要回到主键索引捞其他字段。只查需要的列,能显著减少 IO 和网络开销 - 多表
JOIN时,ON字段没索引?那第一个被驱动的表可能被全扫一遍。确保所有JOIN条件列都有索引,且顺序匹配联合索引最左前缀 - 子查询(尤其是
WHERE ... IN (SELECT ...))在 MySQL 5.7 及之前版本极易生成临时表,Extra出现Using temporary就得警惕。优先改写成JOIN -
ORDER BY字段不在索引中?会出现Using filesort。复合索引要把排序字段放在最后,例如查WHERE user_id = ? ORDER BY create_time DESC,索引应建为(user_id, create_time)
分页查询 LIMIT OFFSET 越往后越慢
当 LIMIT 10000, 20 这种写法出现时,MySQL 仍需扫描前 10000 行才能跳过——这不是“跳过”,是“读完再扔掉”。
- 用游标方式替代:记录上一页最后一条的
id或create_time,下一页查WHERE id > 12345 LIMIT 20 - 如果必须用 offset,确保
ORDER BY字段有索引,且该索引能覆盖WHERE条件(即形成覆盖索引) - 后台导出类场景,避免用
LIMIT分批,改用主键范围切片,例如WHERE id BETWEEN 100000 AND 110000
真正难的不是写出快 SQL,而是识别哪些查询“看起来没问题但其实很慢”——比如带函数的条件、隐式类型转换、OR 混合索引缺失字段。这些往往藏在业务代码深处,不靠 EXPLAIN 和慢日志,根本发现不了。











