news 2026/5/23 0:28:33

别再瞎建索引了!这个慢查询优化思路值得收藏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再瞎建索引了!这个慢查询优化思路值得收藏

别再瞎建索引了!这个慢查询优化思路值得收藏

数据库性能问题是每个开发和DBA都绑不开的坎儿。线上一个接口响应慢了几秒,用户可能就跑了,老板的脸可能就黑了。今天这篇文章不讲理论空话,直接拿一个真实场景的慢查询出来,一步一步拆解优化过程,从Explain分析到索引重构,把整个调优思路讲透。看完这篇,你下次遇到慢查询,脑子里至少能有一套清晰的排查路线。

SQL调优实战:从Explain分析到索引策略的完整优化案例

一、问题背景:一条查询拖垮了整个订单系统

前段时间接到运维的报警,线上订单查询接口响应时间飙到了8秒以上,高峰期甚至直接超时。这个接口的核心逻辑其实不复杂,就是根据商家ID、订单状态和下单时间范围,去查订单列表。

先把这条SQL贴出来:

sql

SELECT

o.order_id, o.order_no, o.amount, o.status,

u.nickname, u.phone

FROM

t_order o

LEFT JOIN t_user u ON o.user_id = u.user_id

WHERE

o.merchant_id = 10086

AND o.status IN (1, 2, 3)

AND o.create_time BETWEEN '2024-01-01 00:00:00' AND '2024-12-31 23:59:59'

ORDER BY o.create_time DESC

LIMIT 20;

表的数据量大概是:t_order 有1200万条记录,t_user 有800万条记录。没有任何索引的情况下,这条SQL跑一次全表扫描,8秒都算客气的。

二、第一步:用Explain看清查询执行计划

拿到慢查询,第一件事不是急着加索引,而是先用Explain把执行计划拉出来看清楚。

sql

EXPLAIN SELECT

o.order_id, o.order_no, o.amount, o.status,

u.nickname, u.phone

FROM

t_order o

LEFT JOIN t_user u ON o.user_id = u.user_id

WHERE

o.merchant_id = 10086

AND o.status IN (1, 2, 3)

AND o.create_time BETWEEN '2024-01-01 00:00:00' AND '2024-12-31 23:59:59'

ORDER BY o.create_time DESC

LIMIT 20;

执行结果如下:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o ALL NULL NULL NULL NULL 12384521 Using where; Using filesort

1 SIMPLE u eq_ref PRIMARY PRIMARY 8 db.o.user_id 1 NULL

从这张表能看出几个明显的问题:

1、t_order表的type是ALL,意味着全表扫描,扫了1200多万行。

2、Extra里出现了Using filesort,说明ORDER BY没有走索引,MySQL需要额外做一次文件排序。

3、possible_keys是NULL,说明现有索引一个都没用上。

4、rows预估了1200多万行,但实际上LIMIT 20只需要20条,这个差距就是性能问题的根源。

三、第二步:分析WHERE条件,确定索引策略

看WHERE条件里有三个过滤字段:merchant_id、status、create_time。按照MySQL索引的最左前缀原则,索引字段的顺序非常关键。

这里有个核心判断逻辑:

☆ 等值查询的字段放在最前面,范围查询的字段放在后面。因为一旦遇到范围查询(比如BETWEEN、>、<),后面的索引列就无法继续使用了。

所以这三个字段的排序应该是:merchant_id(等值)→ status(等值,但用了IN)→ create_time(范围)。

基于这个思路,我创建了一个联合索引:

sql

ALTER TABLE t_order ADD INDEX idx_merchant_status_time (merchant_id, status, create_time);

加完索引再跑一次Explain:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o ref idx_merchant_status_time idx_merchant_status_time 12 const,const 35621 Using index condition; Using filesort

1 SIMPLE u eq_ref PRIMARY PRIMARY 8 db.o.user_id 1 NULL

进步很明显:

1、type从ALL变成了ref,说明走了索引。

2、rows从1200万降到了3.5万,过滤效率提升了几百倍。

3、但是Extra里还有Using filesort,说明ORDER BY create_time DESC依然没有用上索引排序。

四、第三步:解决filesort问题——让排序也走索引

filesort的出现是因为虽然create_time在联合索引里,但它排在第三位,而MySQL在使用联合索引时,如果前面的字段是等值匹配,理论上是可以利用索引顺序来避免排序的。但这里status用了IN,MySQL的优化器在某些版本中对IN的处理不够理想,导致没有充分利用索引的有序性。

解决办法有两个:

1、把ORDER BY的字段也加到索引的最后面,强制让MySQL走索引排序。但我们的索引已经把create_time放在最后了,理论上应该可以,问题可能出在IN上。

2、换个思路,把ORDER BY的方向和索引方向对齐。当前索引是正序(ASC),但查询用的是DESC。在MySQL 8.0之前,降序扫描索引的支持不太好。不过我们可以通过一个小技巧来解决:把查询改成正序,然后在业务层做反转。

但更直接的方案是,针对这个场景再建一个更精准的索引,把ORDER BY的字段明确放进去,并且考虑覆盖查询:

sql

ALTER TABLE t_order ADD INDEX idx_merchant_status_time_cover (merchant_id, status, create_time, order_id);

这里把order_id也加进去,是为了让查询变成覆盖索引,避免回表。

再看Explain结果:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o ref idx_merchant_status_time_cover idx_merchant_status_time_cover 16 const,const 35621 Using index condition

1 SIMPLE u eq_ref PRIMARY PRIMARY 8 db.o.user_id 1 NULL

关键变化:

1、Extra里的Using filesort消失了!排序问题解决。

2、但是Using index condition还在,说明虽然走了索引,但还是需要回表去查其他字段(amount、order_no等)。

五、第四步:覆盖索引优化——彻底消除回表

回表操作是性能杀手之一。每次通过索引找到主键后,还要再去聚簇索引里查一遍完整行数据,这个开销在大数据量下非常可观。

要彻底消除回表,就得让索引包含查询所需的所有字段,也就是覆盖索引(Covering Index)。

把查询中t_order需要的所有字段都塞进索引:

sql

ALTER TABLE t_order ADD INDEX idx_merchant_full_cover (

merchant_id,

status,

create_time,

order_id,

order_no,

amount

);

这次Explain的结果让人舒适:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o ref idx_merchant_full_cover idx_merchant_full_cover 28 const,const 35621 Using where; Using index

1 SIMPLE u eq_ref PRIMARY PRIMARY 8 db.o.user_id 1 NULL

Extra显示Using index,这就是覆盖索引的标志。t_order表的查询完全不需要回表了,所有字段都能从索引里直接拿到。

最终这条SQL的执行时间从8秒降到了0.03秒,提升了将近270倍。

六、优化前后Explain对比总结

为了让大家更直观地看到变化,把三次Explain的关键指标拉出来做个对比:

优化阶段 type rows Extra 执行时间

优化前(无索引) ALL 12384521 Using where; Using filesort 8.2s

第一次优化(加联合索引) ref 35621 Using index condition; Using filesort 0.8s

最终优化(覆盖索引) ref 35621 Using where; Using index 0.03s

从全表扫描到走索引,从需要回表到覆盖索引,从文件排序到索引排序,每一步优化都有明确的数据支撑。

七、几个容易踩坑的调优细节

在实际调优过程中,还有几个点值得注意:

1、索引不是越多越好。每个索引都会占用存储空间,而且会增加写入操作的开销(INSERT/UPDATE/DELETE都要维护索引)。这次优化只加了一个索引,但解决了所有问题,没必要建一堆冗余索引。

2、IN和OR要慎用。status IN (1, 2, 3)在这个场景下问题不大,但如果IN的值特别多,优化器可能会直接放弃索引走全表扫描。这种情况下可以考虑改写成UNION ALL。

3、LIMIT配合ORDER BY时,如果没有合适的索引,MySQL需要扫描大量数据后再排序再取前N条。所以索引的字段顺序一定要和ORDER BY对齐。

4、覆盖索引虽然好,但索引字段太多会导致索引体积变大,影响内存缓存命中率。需要在查询性能和存储开销之间做平衡。

5、Explain的rows只是估算值,不是精确值。实际执行行数可能和rows有偏差,但数量级一般是准确的,足以用来判断优化方向。

八、写在最后

SQL调优这件事,说难也难,说简单也简单。难的是面对复杂业务场景时不知道从哪下手,简单的是只要你掌握了Explain分析、索引策略、覆盖索引这几板斧,大部分慢查询都能解决。

核心思路就一句话:先用Explain看清执行计划,再根据最左前缀原则和覆盖索引的思想去调整索引,最后用Explain验证优化效果。

别一上来就瞎加索引,也别觉得全表扫描就没救了。慢查询优化的本质,就是让MySQL用最少的IO、最少的计算拿到你要的数据。把这个目标刻在脑子里,调优的时候就不会迷路。

✅ 最终选用标题:一条SQL拖垮系统?Explain三步调优从8秒干到0.03秒

💡注意:本文所介绍的软件及功能均基于公开信息整理,仅供用户参考。在使用任何软件时,请务必遵守相关法律法规及软件使用协议。同时,本文不涉及任何商业推广或引流行为,仅为用户提供一个了解和使用该工具的渠道。

你在生活中时遇到了哪些问题?你是如何解决的?欢迎在评论区分享你的经验和心得!

希望这篇文章能够满足您的需求,如果您有任何修改意见或需要进一步的帮助,请随时告诉我!

感谢各位支持,可以关注我的个人主页,找到你所需要的宝贝。

博文入口:https://blog.csdn.net/Start_mswin 复制到【浏览器】打开即可,宝贝入口:https://pan.quark.cn/s/b42958e1c3c0 宝贝:https://pan.quark.cn/s/1eb92d021d17

作者郑重声明,本文内容为本人原创文章,纯净无利益纠葛,如有不妥之处,请及时联系修改或删除。诚邀各位读者秉持理性态度交流,共筑和谐讨论氛围~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 0:24:02

千问 LeetCode 2561. 重排水果 Python3实现

这道题的核心是贪心 利用全局最小值做中介交换。下面给出Python3实现&#xff0c;附带详细注释。from typing import List from collections import Counterclass Solution:def minCost(self, basket1: List[int], basket2: List[int]) -> int:# 1. 统计每个水果在两个篮子…

作者头像 李华
网站建设 2026/5/23 0:21:46

智能集群脑 · 用 Multi-Agent 让代码审查快10倍

&#x1f9d1;‍&#x1f4bb; 博主介绍 & 诚邀关注 作者&#xff1a;专注于 Java、Python、前端开发的技术博主 | 全网粉丝 30 万 在校期间协助导师完成毕业设计课题分类、论文格式初审及代码整理工作&#xff1b;工作后持续分享毕设思路&#xff0c;助力毕业生顺利完成…

作者头像 李华
网站建设 2026/5/23 0:16:17

如何让你的开源项目被更多人看到?推广比写代码更难

对于软件测试从业者而言&#xff0c;打造一款解决行业痛点的开源测试工具、框架或测试用例库&#xff0c;往往需要耗费数月甚至数年的心血&#xff1a;从需求梳理、框架设计到代码开发、多场景测试验证&#xff0c;每一步都凝聚着专业经验。但很多人都会遭遇相同的困境&#xf…

作者头像 李华
网站建设 2026/5/23 0:16:14

面向软件测试从业者的开源许可证选择与合规指南

在软件测试的日常工作中&#xff0c;开源组件早已无处不在。从 Selenium、Appium 到 JMeter、Gatling&#xff0c;从 pytest、JUnit 到各种 Mock 框架&#xff0c;再到容器镜像中内置的工具链——它们构成了测试体系的骨架。然而&#xff0c;当你在测试脚本里引入一个开源库&am…

作者头像 李华
网站建设 2026/5/23 0:11:24

如何免费激活Windows和Office:3步实现永久激活的终极指南

如何免费激活Windows和Office&#xff1a;3步实现永久激活的终极指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows激活弹窗烦恼吗&#xff1f;是否遇到过Office突然变成只读模式…

作者头像 李华