news 2026/5/23 9:55:39

我用Explain揪出了藏了半年的性能杀手

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
我用Explain揪出了藏了半年的性能杀手

我用Explain揪出了藏了半年的性能杀手

我用Explain揪出了藏了半年的性能杀手

同样一条SQL语句,有人写完跑了30秒还在转圈,有人花5分钟调优后0.02秒就出结果。这中间差的不是运气,是方法。今天这篇文章,我把这些年踩过的坑、总结出的SQL优化思路,一次性全掏给你。不讲虚的,全是实战案例和可直接复用的代码,看完就能上手。

一、SQL优化核心思路与Explain对比分析

1、为什么要学Explain

很多人写完SQL就觉得完事了,至于为什么慢、慢在哪儿,完全靠猜。其实MySQL早就给我们准备好了一把"透视镜"——EXPLAIN命令。它能让你看到查询执行计划的每一个细节:全表扫描还是走了索引、扫描了多少行、有没有用到临时表。说白了,不会看Explain,优化SQL就跟闭着眼开车一样。

下面这张表是我整理的Explain关键字段含义对照,建议收藏:

字段名 含义说明 关注重点

id 查询的序列号,相同id表示从属关系 复杂查询时注意执行顺序

select_type 查询类型(SIMPLE、PRIMARY、SUBQUERY等) 判断是否有子查询

table 当前操作的表名 —

type 访问类型,性能从好到差:system > const > eq_ref > ref > range > index > ALL 最核心字段,至少要达到range级别

possible_keys 可能用到的索引 看有没有命中预期索引

key 实际使用的索引 和possible_keys对比看是否选对了

key_len 索引使用的字节数 判断联合索引是否完全命中

rows 预估需要扫描的行数 越小越好

Extra 额外信息 出现Using filesort、Using temporary时要警惕

2、一个真实的Explain对比案例

我之前接手过一个订单查询接口,原始SQL是这样的:

sql

SELECT o.order_id, o.user_id, o.amount, u.nickname, u.level

FROM orders o

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

WHERE o.status = 1

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

ORDER BY o.create_time DESC

LIMIT 20;

这条SQL在数据量达到200万行时,查询耗时超过8秒。我用EXPLAIN看了一下执行计划:

id select_type table type possible_keys key key_len rows Extra

1 SIMPLE o ALL idx_status NULL NULL 1850000 Using where; Using filesort

1 SIMPLE u eq_ref PRIMARY PRIMARY 4 1 —

问题一目了然:orders表走了全表扫描(type=ALL),扫描了185万行,还用了filesort排序。这能不慢吗?

优化之后的SQL:

sql

SELECT o.order_id, o.user_id, o.amount, u.nickname, u.level

FROM orders o

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

WHERE o.status = 1

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

ORDER BY o.create_time DESC

LIMIT 20;

我做了两件事:第一,在orders表上建了联合索引idx_status_createtime (status, create_time);第二,SQL本身没有改,因为逻辑是对的,问题出在索引上。

再次EXPLAIN:

id select_type table type possible_keys key key_len rows Extra

1 SIMPLE o range idx_status_createtime idx_status_createtime 9 3200 Using index condition

1 SIMPLE u eq_ref PRIMARY PRIMARY 4 1 —

type从ALL变成了range,扫描行数从185万降到3200,查询时间直接从8秒掉到0.03秒。这就是索引策略带来的降维打击。

二、索引策略示例:联合索引到底怎么建

1、最左前缀原则是铁律

联合索引(a, b, c),查询条件必须从最左边开始匹配才能生效。WHERE a = 1 AND b = 2能用到索引,但WHERE b = 2 AND c = 3就用不上。很多人建索引喜欢把常用字段全塞进去,结果索引又大又慢,因为MySQL只能从最左边开始用。

举个例子,我见过一个同学在用户表上建了(age, city, gender, name)四列联合索引,结果他的查询是:

sql

SELECT * FROM users WHERE city = 'Beijing' AND gender = 1;

这条SQL压根用不上这个索引,因为最左边的age没有出现。后来我建议他把city放到最左边,改成(city, gender, age, name),问题立刻解决。

2、覆盖索引能省掉回表操作

回表这个概念很多人知道但不重视。简单说,如果查询需要的字段都在索引里,MySQL就不需要再去查主键索引(也就是不需要"回表"),这叫覆盖索引。

比如上面的订单查询,我把索引改成(status, create_time, order_id, user_id, amount),把SELECT要的字段都包进去,EXPLAIN的Extra里就会出现Using index,说明完全不需要回表:

id select_type table type possible_keys key key_len rows Extra

1 SIMPLE o range idx_status_createtime idx_status_createtime 9 3200 Using index condition; Using index

Extra里同时出现Using index condition和Using index,这就是覆盖索引的标志。

3、索引不是越多越好

这个坑我自己也踩过。曾经有张表我建了6个索引,以为查什么都快。结果写入性能暴跌,因为每插入一行数据,MySQL要维护6棵B+树。后来我用SHOW INDEX FROM table_name把所有索引列出来,发现有3个索引的使用频率极低,直接删掉之后,写入速度提升了40%,查询性能几乎没受影响。

三、查询优化案例:那些年我遇到的奇葩慢查询

1、LIKE左模糊是索引杀手

sql

SELECT * FROM users WHERE name LIKE '%张%';

这条SQL如果name上有普通索引,基本等于没用。因为B+树是按前缀排序的,你让它从中间开始找,它只能全表扫描。解决办法有两个:要么用全文索引(FULLTEXT),要么改业务逻辑,比如让用户输入姓名的前几个字来搜。

2、OR条件可能导致索引失效

sql

SELECT * FROM orders WHERE status = 1 OR status = 3;

很多人以为status上有索引就能用,但实际上当OR两边的字段不一致时,MySQL可能会放弃索引。更好的写法是用UNION ALL:

sql

SELECT * FROM orders WHERE status = 1

UNION ALL

SELECT * FROM orders WHERE status = 3;

这样每条分支都能走索引,实际测试下来比OR快了将近3倍。

3、子查询改写成JOIN

这个案例我印象特别深。有条SQL是这样的:

sql

SELECT * FROM orders

WHERE user_id IN (

SELECT user_id FROM users WHERE level >= 5

);

当users表有10万行、orders表有200万行时,这条SQL跑了15秒。把它改成JOIN:

sql

SELECT o.*

FROM orders o

INNER JOIN users u ON o.user_id = u.user_id

WHERE u.level >= 5;

同样的逻辑,改完之后只要0.8秒。原因是MySQL对子查询的优化在老版本里很差,JOIN的执行计划通常更优。

四、Explain实战对比:优化前后差距有多大

我把前面几个案例的优化前后数据汇总成表格,这样对比更直观:

案例 优化前type 优化前rows 优化后type 优化后rows 耗时对比

订单查询 ALL 1850000 range 3200 8s → 0.03s

姓名模糊搜索 ALL 500000 fulltext 1 4s → 0.01s

OR条件查询 ALL 320000 ref 160000 2.5s → 0.8s

子查询改写 ALL 2000000 ref 48000 15s → 0.8s

你看,同样是SQL调优,思路对了,性能提升不是百分之几十的事,是几十倍甚至上千倍的事。

五、日常优化的几个实用习惯

☆ 1、慢查询日志一定要开。在my.cnf里设置slow_query_log = 1和long_query_time = 1,把超过1秒的查询都记下来,定期分析。

☆ 2、建索引之前先用EXPLAIN验证。不要凭感觉建索引,先写好SQL,看执行计划,确认索引能被用到再建。

☆ 3、定期用ANALYZE TABLE table_name更新统计信息。MySQL的查询优化器依赖统计信息来选执行计划,数据变动大了但统计信息没更新,就可能选错索引。

☆ 4、能用EXISTS就别用IN。当子查询结果集很大时,EXISTS一旦找到匹配就会停止扫描,而IN要把所有结果都拿出来比对。

六、写在最后

SQL优化这件事,说难也难,说简单也简单。难的是每个业务场景都不一样,需要具体问题具体分析;简单的是方法论就那么几条:看Explain、建对索引、少用函数、避免全表扫描。把这几条刻进脑子里,遇到慢查询的时候就不会慌。

希望这篇文章能帮你少走一些弯路。如果你手里也有经典的SQL优化案例,欢迎在评论区一起交流。

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

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

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

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

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

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

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

强力突破:Radioconda一站式解决软件无线电开发环境配置难题

强力突破:Radioconda一站式解决软件无线电开发环境配置难题 【免费下载链接】radioconda-installer Software radio distribution and installer for conda 项目地址: https://gitcode.com/gh_mirrors/ra/radioconda-installer 你是否曾花费数小时甚至数天时…

作者头像 李华
网站建设 2026/5/23 9:49:07

带标注的瓶盖缺陷分类数据集, 识别率99.3%,可识别瓶盖破损,瓶盖松动,无瓶盖,密封环断裂等常见问题,支持yolo,coco json,pascal voc xml格式

带标注的瓶盖缺陷分类数据集, 识别率99.3%,可识别瓶盖破损,瓶盖松动,无瓶盖,密封环断裂等常见问题,支持yolo,coco json,pascal voc xml格式 模型训练指标参数: 数据集拆…

作者头像 李华
网站建设 2026/5/23 9:44:12

大牛直播SDK(SmartMediaKit)Windows平台多路RTSP转RTMP推流集成说明

文档概述 在安防监控、智慧园区、应急指挥、工业视觉、低空经济、无人机回传和多路摄像头上云等场景中,现场设备通常以 RTSP 方式输出视频流,而云端平台、直播分发平台或业务中台往往更倾向于接收 RTMP 流。此时,系统需要在边缘侧或 Windows…

作者头像 李华
网站建设 2026/5/23 9:42:52

JavaScript DOM 核心操作(DOM Manipulation)

本文是 JavaScript DOM 核心权威教程,涵盖 DOM 树、节点选择、遍历、增删改查、属性、样式、事件、渲染流程与性能优化。一、什么是 DOM?DOM(文档对象模型) 是浏览器将 HTML 解析成的对象化树形结构,是 JavaScript 操作…

作者头像 李华
网站建设 2026/5/23 9:38:35

pprint,一个漂亮打印的 Python 库!

在日常编程中,我们经常需要打印复杂的数据结构——嵌套的字典、列表、JSON 响应、配置对象等。使用普通的 print() 会将整个结构挤在一行或简单换行,导致可读性极差,尤其是在调试多层嵌套的 API 返回数据时,简直是一场灾难。pprin…

作者头像 李华
网站建设 2026/5/23 9:35:47

3步掌握WeChatExporter:永久备份微信聊天记录的终极方案

3步掌握WeChatExporter:永久备份微信聊天记录的终极方案 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否曾因手机丢失或更换设备而痛失珍贵的微信聊天记…

作者头像 李华