当前位置: 首页 > news >正文

一条慢查询毁了整个接口,我用三步把它救活了

一条慢查询毁了整个接口,我用三步把它救活了

说真的,每次线上系统突然卡顿,排查到最后十有八九都是SQL的锅。我曾经为了优化一条订单查询语句,从执行计划看了三天,把索引改了五版,最后才发现问题根本不在索引上。今天这篇文章,我就拿一个真实的慢查询案例,手把手带你走一遍SQL调优的完整思路,保证你看完之后,再遇到慢查询心里就有底了。

一、问题的发现:一条SQL拖垮了整个接口

事情是这样的,我们有一个订单列表查询接口,平时响应时间在50毫秒左右,某天上午突然飙到了3秒以上,用户那边直接报超时了。

先把这条SQL贴出来,大家感受一下:

sql

SELECT

o.id, o.order_no, o.user_id, o.total_amount, o.status,

u.nickname, u.phone

FROM order o

LEFT JOIN user u ON o.user_id = u.id

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

AND o.create_time >= '2025-01-01 00:00:00'

AND o.create_time <= '2025-12-31 23:59:59'

ORDER BY o.create_time DESC

LIMIT 20;

这条SQL看起来非常普通对吧?但它的执行时间从原来的几十毫秒直接飙到了2秒多,这背后到底发生了什么?

☆ 关键线索:order表数据量大约有800万条,user表大约有120万条。

二、第一步:用Explain看透执行计划

调优的第一步永远是看执行计划,别上来就改索引,那是瞎蒙。

sql

EXPLAIN SELECT

o.id, o.order_no, o.user_id, o.total_amount, o.status,

u.nickname, u.phone

FROM order o

LEFT JOIN user u ON o.user_id = u.id

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

AND o.create_time >= '2025-01-01 00:00:00'

AND o.create_time <= '2025-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 idx_status,idx_create_time NULL NULL NULL 7823456 Using where; Using filesort

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

一看就炸了。order表的type是ALL,意思是全表扫描,扫描了将近782万行数据。Extra里写着Using filesort,说明排序没有走索引,额外做了文件排序。

这就是慢的根源:全表扫描 + 文件排序,双重暴击。

三、第二步:分析瓶颈,制定调优策略

先别急着加索引,我们得想清楚几个问题:

1、WHERE条件里有status和create_time两个字段,到底该建什么索引?

2、ORDER BY create_time DESC,排序字段能不能和索引配合?

3、LIMIT 20说明只要前20条,能不能利用这个特性优化?

☆ 核心思路:既然查询有范围条件 + 排序 + 分页,最理想的情况是建一个联合索引,让MySQL能直接按索引顺序取数据,避免全表扫描和文件排序。

四、第三步:构建最优索引并验证效果

根据上面的分析,我建了一个联合索引:

sql

ALTER TABLE `order`

ADD INDEX idx_status_create_time (status, create_time);

为什么是(status, create_time)而不是反过来?这里有个很重要的原则:

1、等值条件放前面,范围条件放后面。 status IN (1,2,3)虽然是多值,但本质上还是等值匹配,应该放在联合索引的最左边。create_time是范围查询,放在后面。

2、排序字段要能接上索引。 因为ORDER BY create_time DESC,而索引里create_time在第二位,当status确定之后,create_time本身是有序的,MySQL可以直接按索引顺序往后读,不需要额外排序。

再看一次执行计划:

id select_type table type possible_keys key key_len ref rows Extra

1 SIMPLE o range idx_status_create_time idx_status_create_time 12 NULL 156842 Using index condition

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

效果立竿见影:

1、type从ALL变成了range,走了索引范围扫描。

2、扫描行数从782万降到了15万左右,降了将近50倍。

3、Extra里Using filesort消失了,排序直接走索引。

执行时间从2.3秒直接降到了45毫秒。

五、第四步:还能不能再优化?深挖一下

到这里其实已经够用了,但作为一个有追求的人,我又多想了一步。

☆ 问题:status IN (1,2,3)匹配的数据有15万行,但我们只要最新的20条。MySQL虽然走了索引,但还是要扫描15万行来找到这20条,有没有办法让它只扫描20行?

答案是:改业务逻辑,用子查询先定位,再关联。

sql

SELECT

o.id, o.order_no, o.user_id, o.total_amount, o.status,

u.nickname, u.phone

FROM (

SELECT id, order_no, user_id, total_amount, status

FROM `order`

WHERE status IN (1, 2, 3)

AND create_time >= '2025-01-01 00:00:00'

AND create_time <= '2025-12-31 23:59:59'

ORDER BY create_time DESC

LIMIT 20

) o

LEFT JOIN user u ON o.user_id = u.id;

这个改写的核心在于:先在order表上利用索引直接取出最新的20条ID,然后再去关联user表。这样order表的扫描行数就从15万降到了20行。

再看执行计划:

id select_type table type possible_keys key key_len ref rows Extra

1 PRIMARY ALL NULL NULL NULL NULL 20 NULL

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

2 DERIVED order index idx_status_create_time idx_status_create_time 12 NULL 20 Using where

看到没?order表的rows直接变成了20,type变成了index,说明完全按索引顺序在取数据,一行多余的都没扫。

执行时间进一步降到了8毫秒。

六、几个容易踩坑的点,提前避一避

1、☆ 联合索引的字段顺序不能乱。很多人喜欢把create_time放前面,觉得时间范围查询很常见。但一旦create_time在第一位,status就用不上索引了,因为范围查询之后的字段索引失效。这是MySQL最经典的"最左前缀"规则。

2、☆ IN和=在索引使用上是一样的,都算等值匹配。但如果IN里面的值特别多,比如IN (1,2,3,4,5,6,7,8,9,10),MySQL可能会认为不如全表扫描,这时候可以考虑用UNION ALL改写。

3、☆ LEFT JOIN的时候,右表一定要有索引。上面的例子里user.id是主键,天然有索引,所以没问题。但如果右表的关联字段没有索引,那又是一个大坑。

4、☆ LIMIT优化一定要配合子查询使用,不然MySQL还是会先找到所有符合条件的行,再取前N条。这个技巧在数据量大的时候特别管用。

七、总结一下这次调优的完整思路

步骤 操作 效果

1 用Explain看执行计划 发现全表扫描 + 文件排序

2 分析WHERE条件和排序字段 确定索引字段和顺序

3 建联合索引(status, create_time) 扫描行数从782万降到15万

4 用子查询改写,先取ID再关联 扫描行数从15万降到20

最终结果 执行时间从2.3秒降到8毫秒 提升约280倍

说白了,SQL调优这事儿没有什么黑魔法,就是三步:看计划、建对索引、改写SQL。但每一步都需要你真正理解MySQL是怎么执行查询的,而不是靠感觉瞎猜。

希望这篇文章能帮你在遇到慢查询的时候,少走一些弯路。毕竟,生产环境的每一秒卡顿,背后可能都是一条没优化好的SQL在作妖。

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

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

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

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

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

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

http://www.jsqmd.com/news/821278/

相关文章:

  • 股市均线全解:种类、含义、计算、用法
  • 保姆级教程:用SDK Manager给Jetson AGX Xavier刷机,从连接主机到换国内源一步到位
  • Windows热键冲突终极解决方案:3分钟快速定位占用程序完整指南
  • 2026国内漏水维修TOP5!上海嘉定闵行宝山等地公司专业值得选 - 十大品牌榜
  • taotoken token plan套餐详解如何为长期项目锁定优惠成本
  • AI绘画提示词优化:sd-webui-chatgpt插件实战指南
  • 2026年4月管托批发厂家推荐,保温管托/螺栓管夹/固定管托/隔热管托/支吊架/导向管托/管夹/管托,管托生产厂家哪家好 - 品牌推荐师
  • 如何高效使用yuzu模拟器:在PC上畅玩Switch游戏的完整指南
  • 【C/C++】libusb实战:从零构建ADB USB通信框架
  • 3分钟搞定Figma中文界面:设计师亲自翻译的免费汉化终极指南
  • 管理中台篇六:前端工作台与 Niond UI 迁移
  • 通过curl命令直接调试Taotoken大模型API的简易方法
  • Taotoken模型广场如何辅助开发者进行技术选型
  • NomNom存档编辑器:5个关键技巧让你的《无人深空》体验焕然一新
  • 告别付费!手把手教你用Matrikon OPC Server Simulation(v1.7.2)搭建免费工业数据模拟环境
  • 三步解锁QQ音乐加密文件:让您的音乐真正属于自己
  • 光学仿真终极指南:如何用Python实现严格耦合波分析(RCWA)
  • 2026年5月新发布:塑钢墙板优质服务商盘点与选择指南 - 2026年企业推荐榜
  • 构建高效QA技能库:从自动化测试到CI/CD的实战指南
  • Shield TV救砖指南:开机卡在谷歌验证?这几条ADB命令可能是你的救命稻草
  • 图解朱刘算法:用Python手搓最小树形图,搞定有向图最小生成树
  • 2026年无锡白蚁防治:别墅专属预防方案,无损防护更长效 - 速递信息
  • 从Arduino到MicroPython:给树莓派Pico/RP2040新手的第一个项目避坑指南
  • 如何用ROFL-Player解锁英雄联盟历史回放:终极兼容解决方案
  • 2026年5月更新:变形缝线上实力供应商“城阳区佳和时光建材经营部”深度解析 - 2026年企业推荐榜
  • 网盘下载新革命:9大平台一键解锁,从此告别龟速时代!
  • 揭秘百联OK卡回收流程:快速交易及实用回收技巧 - 团团收购物卡回收
  • 2026年灵芝孢子油品牌对比:头部玩家选购指南 - 速递信息
  • Poppins几何字体:开源多语言排版的技术革新与实战应用
  • Windows + Claude Code + DeepSeek V4 配置记录