【MySQL飞升篇】MySQL性能拉胯?教你用EXPLAIN+慢查询日志,快速搞定优化
SQL调优实战:从执行计划到慢查询优化 本文深入解析SQL性能调优核心技巧: EXPLAIN工具:通过分析执行计划的关键字段(type、rows、key、Extra),精准定位SQL低效原因,重点解读访问类型(ALL到const的效率分级)和常见问题(如Using filesort)。 慢查询日志:配置开启慢查询日志,捕获执行超时的SQL,结合执行时间、扫描行数等指标快速定位性能瓶颈。 实战案例:
引言
高并发、海量数据场景下,一句慢SQL就能拖垮整个系统,这是每个后端开发者和架构师都避不开的坑。很多人遇到性能瓶颈时,要么盲目加索引,要么乱改SQL,结果越调越糟。其实核心秘诀就两个:读懂执行计划、找准慢查询根源。今天就手把手教你吃透EXPLAIN,结合慢查询日志实战优化,从“调优小白”进阶“架构师视角”,搞定SQL性能问题!
文章目录
一、为什么EXPLAIN是SQL调优的“万能钥匙”?
在进行SQL调优前,我们首先要明确:SQL到底是怎么执行的?有没有走索引?有没有做全表扫描?有没有出现低效的排序操作?而EXPLAIN正是MySQL提供的“执行计划分析工具”,只需在SQL语句前加上EXPLAIN,就能直观看到SQL的执行细节,为调优提供精准方向。
💡 重点提示:EXPLAIN不会真正执行SQL语句(除非是SELECT类型,会读取表结构信息),所以不用担心影响线上数据,可放心使用!
举个简单例子,执行 EXPLAIN SELECT * FROM user WHERE age > 20 AND name LIKE '%zhang%'; 后,会输出包含id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra等字段的结果,其中type、rows、key、extra这四个字段是核心中的核心,也是我们调优的重点分析对象。
建议大家先点赞收藏本文,后续调优时直接对照查阅,效率翻倍!👍
二、手把手吃透EXPLAIN四大核心字段
2.1 type:查询类型(效率从低到高排序)
type字段代表MySQL在表中找到所需行的方式,也叫“访问类型”,其值直接决定了SQL的执行效率,常见取值及说明如下(重点记红框内高效类型):
| 类型 | 说明 | 效率等级 |
|---|---|---|
| ALL | 全表扫描,遍历整个表找到匹配行,效率最低 | 差 |
| index | 全索引扫描,遍历整个索引树,比ALL略好(索引文件通常比数据文件小) | 较差 |
| range | 索引范围扫描,只扫描索引的一部分(如between、in、>、<等条件) | 中等 |
| ref | 非唯一索引扫描,返回匹配某个单独值的所有行(如普通索引匹配) | 较好 |
| eq_ref | 唯一索引扫描,对于每个索引键,表中只有一条记录匹配(如主键/唯一键关联) | 好 |
| const/system | 常量查询,表中只有一条记录匹配,效率最高(如根据主键查询单条数据) | 最好 |
实战案例:
-- 1. 全表扫描(type=ALL)
EXPLAIN SELECT * FROM user WHERE age = 25;
-- 原因:age字段未建索引,MySQL只能遍历整个user表
-- 2. 唯一索引扫描(type=eq_ref)
EXPLAIN SELECT u.name, o.order_no FROM user u JOIN order o ON u.id = o.user_id;
-- 原因:u.id是主键(唯一索引),o.user_id关联主键,每次匹配只有一条记录
2.2 rows:预估扫描行数
rows字段表示MySQL预估要扫描的行数,行数越少,执行效率越高。需要注意的是,这是一个预估值,不是精确值,但能直观反映SQL的低效程度。
比如同样是查询用户信息,A SQL的rows=10000,B SQL的rows=10,显然B SQL的效率更高。调优的核心目标之一,就是通过优化索引、调整SQL,让rows值尽可能小。
2.3 key:实际使用的索引
key字段表示MySQL实际执行SQL时使用的索引,如果该字段为NULL,说明SQL没有使用任何索引,大概率是低效查询(除非表数据量极小)。
重点注意:
- possible_keys:表示可能会使用的索引(候选索引),但不一定实际使用;
- key:表示实际使用的索引,只有当key字段有值时,才说明索引真正生效。
如果possible_keys有值但key为NULL,可能是因为索引选择性太差(如性别字段,只有男/女两个值),MySQL认为全表扫描比走索引更高效。
2.4 Extra:额外执行信息(调优关键信号)
Extra字段包含了SQL执行的额外信息,很多时候能直接定位到调优痛点,常见关键值及处理方案如下:
2.4.1 Using filesort(文件排序,严重低效)
含义:MySQL无法利用索引完成排序操作,只能在内存或磁盘中进行文件排序,效率极低,尤其是数据量大时。
优化方案:设计“覆盖索引”(将排序字段和查询字段都包含在索引中),让MySQL能通过索引直接排序,避免文件排序。
2.4.2 Using temporary(临时表,严重低效)
含义:MySQL需要创建临时表来存储中间结果,通常出现在GROUP BY、DISTINCT等操作中,效率低下。
优化方案:优化索引,让GROUP BY/DISTINCT操作能利用索引完成,避免创建临时表。
2.4.3 Using index(覆盖索引,高效)
含义:SQL只需要通过索引就能获取所需数据,不需要回表查询数据行(即“索引覆盖”),效率极高,是调优的理想状态。
2.4.4 Using where(过滤条件)
含义:MySQL先通过索引找到匹配的行,再根据WHERE条件过滤,属于正常情况,但如果结合type=ALL(全表扫描),则需要优化。
三、慢查询日志分析:精准定位低效SQL
光会看EXPLAIN还不够,我们还需要先找到那些“拖慢系统”的慢查询SQL,这就需要用到MySQL的慢查询日志(Slow Query Log)。
3.1 开启慢查询日志
默认情况下,MySQL的慢查询日志是关闭的,我们可以通过以下命令开启(线上环境建议开启,日志存储路径需合理配置):
-- 临时开启(重启MySQL后失效)
SET GLOBAL slow_query_log = ON; -- 开启慢查询日志
SET GLOBAL long_query_time = 1; -- 设定慢查询阈值(超过1秒的SQL记录)
SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log'; -- 日志存储路径
-- 永久开启(修改my.cnf配置文件,重启MySQL生效)
[mysqld]
slow_query_log = ON
long_query_time = 1
slow_query_log_file = /var/lib/mysql/slow.log
3.2 解读慢查询日志
慢查询日志会记录下所有执行时间超过long_query_time的SQL,每条记录包含执行时间、用户、主机、SQL语句等信息,示例如下:
# Time: 2026-02-03T10:00:00.000000Z
# User@Host: root[root] @ localhost [] Id: 1234
# Query_time: 2.500000 Lock_time: 0.000000 Rows_sent: 10 Rows_examined: 10000
SET timestamp=1770000000;
SELECT * FROM order WHERE create_time > '2026-01-01' ORDER BY total_amount DESC;
- Query_time:执行时间(2.5秒,超过阈值1秒,被记录);
- Rows_examined:扫描行数(10000行,效率低);
- Rows_sent:返回行数(10行,说明大量扫描的行未被返回)。
通过慢查询日志,我们能快速定位到哪些SQL是“性能杀手”,然后针对性地用EXPLAIN分析优化。
四、实战优化:搞定Using filesort案例
结合前面的知识,我们通过一个真实案例,手把手教大家如何优化包含Using filesort的慢查询。
4.1 问题场景
有一个订单表(order),包含字段:id(主键)、user_id(用户ID)、create_time(创建时间)、total_amount(订单金额)、status(订单状态),数据量10万条。执行以下SQL时,查询时间超过2秒,且EXPLAIN显示存在Using filesort:
-- 慢查询SQL
SELECT id, total_amount, status FROM order
WHERE user_id = 100 AND create_time > '2026-01-01'
ORDER BY total_amount DESC;
4.2 分析过程(EXPLAIN)
执行 EXPLAIN 上述SQL,得到核心结果:
- type:ref(user_id字段已建普通索引);
- key:idx_user_id(使用了user_id的索引);
- Extra:Using filesort(存在文件排序,低效)。
原因:虽然user_id字段有索引,但排序字段是total_amount,MySQL无法利用idx_user_id索引完成排序,只能在查询后进行文件排序,导致执行效率低。
4.3 优化方案:创建覆盖索引
设计一个“联合索引”,包含查询条件字段(user_id、create_time)和排序字段(total_amount),同时包含查询结果字段(id、status),让MySQL能通过索引直接完成查询和排序,避免文件排序。
创建联合索引:
CREATE INDEX idx_user_create_total ON order (user_id, create_time, total_amount);
- 索引顺序说明:先放等值查询字段(user_id),再放范围查询字段(create_time),最后放排序字段(total_amount);
- 覆盖性:该索引包含了SQL中的查询条件(user_id、create_time)、排序字段(total_amount)和返回字段(id是主键,InnoDB中主键会自动包含在二级索引中,status需包含在索引中?不,InnoDB二级索引叶子节点存储主键,查询status需回表?这里调整:将返回字段也包含进索引,形成覆盖索引)。
修正联合索引(包含返回字段status):
CREATE INDEX idx_user_create_total_status ON order (user_id, create_time, total_amount, status);
4.4 优化后验证
再次执行EXPLAIN分析优化后的SQL,核心结果:
- type:range(user_id等值匹配,create_time范围查询,走索引);
- key:idx_user_create_total_status(使用了新创建的联合索引);
- rows:10(预估扫描行数大幅减少);
- Extra:Using index(覆盖索引,无文件排序)。
查询时间从2.5秒缩短至0.01秒,优化效果显著!🎉
五、总结
本文从架构师视角出发,围绕SQL调优核心,讲解了EXPLAIN四大核心字段(type、rows、key、extra)的解读方法,结合慢查询日志的开启与分析,通过真实案例实战了Using filesort的优化技巧。核心要点如下:
- EXPLAIN是SQL调优的核心工具,重点关注type(优先eq_ref、ref、range)、rows(越少越好)、key(非NULL)、extra(避免Using filesort/Using temporary);
- 慢查询日志能精准定位低效SQL,线上环境建议开启,阈值根据业务场景调整;
- 优化核心思路:通过合理设计索引(如联合索引、覆盖索引),让SQL尽可能走索引,减少扫描行数和额外操作。
更多推荐

所有评论(0)