MySQL 慢查询怎么定位:从 EXPLAIN 到组合索引
1. 引言
适合订单列表、用户记录、日志表这类按条件筛选并排序的查询优化。
接口偶尔变慢时,很多人会先怀疑服务器配置。实际项目里,更常见的原因是 SQL 没走索引,或者走了一个不合适的索引。尤其是"按用户筛选,再按时间倒序取最新记录"的接口,数据量一上来就容易拖慢。
这篇用一条订单查询做例子,从记录 SQL、查看执行计划、补组合索引到复测耗时,走一遍完整流程。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
2. 先拿到真实 SQL
优化前要拿到接口实际执行的 SQL,而不是凭印象改。ORM 生成的语句可能带额外字段、默认排序或软删除条件,这些都会影响索引选择。
-- 示例慢查询
SELECT *
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
LIMIT 20;
这类 SQL 的目标很明确:先筛出某个用户的订单,再按创建时间倒序拿最近 20 条。索引也应该围绕这个访问路径设计。
3. 用 EXPLAIN 看扫描方式
执行 EXPLAIN 后,重点看 type、rows、key。如果 type 是 ALL,说明可能在全表扫描;如果 rows 很大,说明数据库需要检查大量记录才能返回结果。
-- 查看执行计划
EXPLAIN
SELECT *
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
LIMIT 20;
在订单表有百万级数据时,全表扫描会非常明显。即使最终只返回 20 行,数据库也可能先扫很多行,再排序,再截取结果。

4. 补一个符合查询顺序的索引
这条查询既有等值条件,又有排序条件。单独给 user_id 建索引可以减少筛选范围,但排序仍可能额外发生。更合适的是组合索引:把 user_id 放前面,把 created_at 放后面。
-- 添加组合索引
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at DESC);
索引顺序要贴合查询条件。user_id 是等值筛选,放在前面;created_at 是排序字段,接在后面。这样数据库可以沿着索引直接拿到目标用户的最新订单。

不要看到慢查询就无脑加索引。每个索引都会增加写入成本和存储占用,线上表加索引前要评估数据量、锁表风险和业务低峰窗口。
5. 复测执行计划和接口耗时
索引加完后,再跑一次 EXPLAIN。理想情况下,key 会命中新建的组合索引,type 不再是 ALL,rows 也会明显下降。
-- 复测查询
EXPLAIN
SELECT *
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
LIMIT 20;
执行计划只是第一步,还要看接口真实耗时。建议在测试环境用相近数据量复现,再观察线上慢查询日志。只要查询条件稳定,组合索引通常能把这类列表接口从秒级压到毫秒级。
6. 顺手检查 SELECT 字段
SELECT * 在教程里写起来方便,但生产代码里建议只取页面需要的字段。字段越多,回表和网络传输的成本越高。列表页通常只需要订单号、金额、状态、创建时间,不一定要把备注、地址、扩展 JSON 一起查出来。
7. 总结
慢查询优化不是只会加索引。拿到真实 SQL、看执行计划、设计索引、复测耗时,这四步走完整,才算把问题闭环。
更多推荐

所有评论(0)