MySQL 慢查询优化|explain执行计划详解

MySQL 慢查询优化全套实战,从慢查询日志开启、定位慢 SQL,到 explain 执行计划逐字段详解,重点讲解 type、key、rows、Extra 字段;覆盖索引失效场景、优化手段、高频题,大量真实 SQL 案例。

前言

MySQL 性能优化是后端面试必考重点,日常开发中最头疼的就是慢 SQL。 一条没加索引、索引失效、或者写法不合理的 SQL,在数据量大的时候会产生全表扫描,数据库 CPU 飙升、接口超时、服务雪崩。

优化慢 SQL 的标准流程:

  1. 开启慢查询日志,捕获执行时间过长的 SQL
  2. 使用 explain 分析 SQL 执行计划,定位瓶颈
  3. 根据执行计划优化索引、改写 SQL
  4. 验证优化效果,压测对比

本文重点讲解explain执行计划,结合实战案例,看懂每一列含义,学会判断 SQL 好坏,找到优化方向。

环境:MySQL 5.7 / 8.0 通用

一、慢查询日志基础(怎么找到慢 SQL)

1.1 查看慢查询相关参数

sql 复制代码
-- 查看是否开启慢查询日志
show variables like 'slow_query_log';
-- 慢查询阈值,单位秒,默认10s
show variables like 'long_query_time';
-- 慢日志文件路径
show variables like 'slow_query_log_file';
-- 记录没有使用索引的SQL
show variables like 'log_queries_not_using_indexes';

1.2 临时开启(重启失效)

sql 复制代码
set global slow_query_log = ON;
set global long_query_time = 1; -- 超过1秒就记录
set global log_queries_not_using_indexes = ON;

生产建议:long_query_time 设置 1 秒;不要长期开启log_queries_not_using_indexes,日志量巨大。
工具:mysqldumpslow 慢日志分析工具,汇总统计慢 SQL。

找到慢 SQL 之后,最重要的工具就是 explain,在 SQL 前面加上 explain,看执行计划,MySQL 不会真正执行这条 SQL,只是输出执行方案。

sql 复制代码
explain select * from user where id = 1;

二、explain 输出字段总览

执行 explain 会返回 12 列,核心重点:id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra

字段 含义
id 查询的序列号,代表 SQL 执行顺序
select_type 查询类型(简单查询 / 子查询 / 联合查询)
table 当前访问哪张表
type 最重要!访问类型,判断索引好坏
possible_keys 理论上可以用到的索引
key 实际使用的索引,NULL 代表没走索引
key_len 使用索引的长度(字节),判断联合索引使用情况
ref 索引匹配的列或常量
rows 预估扫描行数,越小越好
filtered 满足条件的数据百分比
Extra 额外信息,包含关键提示,如 Using filesort、Using temporary

三、逐字段详细讲解

3.1 id

id 越大,越先执行;id 相同,从上往下顺序执行。

  • id 相同:并列,从上到下执行
  • id 不同:子查询,id 越大优先执行
  • id 有 null:union 联合查询,最后执行

3.2 select_type 查询类型

  1. SIMPLE:简单查询,没有子查询、union,最常见
  2. PRIMARY:外层主查询
  3. SUBQUERY:子查询(select 里的子查询)
  4. DERIVED:派生表,from 里面的子查询(临时表)
  5. UNION:union 中第二个及之后的查询
  6. UNION RESULT:union 合并结果

3.3 table

当前这一行执行计划对应的表名。<derived2>代表 id=2 的派生临时表。

3.4 type【重中之重,索引好坏核心判断】

性能从优到劣排序: system > const > eq_ref > ref > range > index > ALL

  • system:表只有一行数据,极少出现
  • const :主键 / 唯一索引等值匹配,最多一行,常量。where id=1
  • eq_ref:唯一索引,关联查询,一对一匹配(join 主键)
  • ref :普通二级索引等值查询,可能匹配多行。日常开发理想状态
  • range :索引范围查询,> < >= <= between in,走索引范围扫描
  • index:索引全扫描,遍历整个索引树,比全表扫描好一点,但依然差
  • ALL :全表扫描,最坏情况,必须优化

优化目标:至少达到 range,最好 ref;尽量避免 index / ALL。

3.5 possible_keys

MySQL 优化器认为有可能可以使用的索引,只是候选,不一定真正使用。

possible_keys 有值,key 为 null:代表有索引,但优化器评估后觉得索引不如全表快,选择不走索引(索引失效场景)。

3.6 key

真正实际使用的索引名称。

  • null:没有使用索引
  • 字段有索引名:成功命中索引

3.7 key_len

索引使用的字节长度,用来判断联合索引命中了几个字段。

规则:

  • varchar (n) utf8mb4:每个字符占 4 字节,varchar (100) = 400 + 2 字节长度头
  • char 固定长度;int 4 字节;bigint 8 字节
  • 可空字段额外 + 1 字节

示例:联合索引 idx_name_age(name,age),如果 key_len 只计算到 name,说明只命中第一个字段,联合索引最左前缀失效。

3.8 ref

索引匹配的对象:常量、字段名。const代表常量匹配。

3.9 rows

MySQL 预估需要扫描多少行才能找到目标数据。

这是预估值,不是真实行数,数值越小越好。rows 越大,代价越高。

3.10 Extra 额外信息【高频坑点都在这里】

重点关注下面几个关键词,看到就要警惕:

  1. Using index :覆盖索引!最优,查询字段全部在索引中,不需要回表。性能很高。
  2. Using where:存储引擎返回数据之后,MySQL 服务层做条件过滤。不代表索引失效。
  3. Using filesort :文件排序,严重性能损耗! 没有使用索引排序,MySQL 额外排序,order by没有走索引时出现。必须优化。
  4. Using temporary :创建临时表,性能差,group by /distinct 没有索引,创建临时表分组。优先优化。
  5. Using index condition:索引条件下推 ICP,MySQL5.6 + 优化,属于好现象。
  6. Impossible where:where 条件永远不成立,不会返回数据。

重点记忆:看到Using filesort、Using temporary代表 SQL 需要优化。

四、实战案例演示

案例 1:主键等值查询(最优 const)

sql 复制代码
explain select * from user where id = 1;

type:const,key: 主键索引,rows:1。

案例 2:普通索引等值查询 ref

user 表建立索引 idx_phone(phone)

sql 复制代码
explain select id,name from user where phone = '13800138000';

type:ref。 如果查询字段只有 id,name,phone,Extra 出现Using index → 覆盖索引,不需要回表。

案例 3:范围查询 range

sql 复制代码
explain select * from user where id > 1000;

type:range。

案例 4:全表扫描 ALL(坏 SQL)

没有索引:

sql 复制代码
explain select * from user where name = '张三';

type:ALL,key:null,全表扫描,数据量大慢查询。

案例 5:Using filesort 排序失效

索引idx_name(name)

sql 复制代码
explain select * from user where name='张三' order by age;

where 走 name 索引,order by age 没有索引,Extra 出现 Using filesort。

优化方案:建立联合索引 idx_name_age(name,age)

五、索引失效常见场景(explain 验证)

  1. 索引列做函数运算、隐式类型转换
sql 复制代码
where substr(name,1,1)='张'; -- 索引失效
where phone=13800138000; -- phone是varchar,传数字,隐式转换索引失效
  1. 联合索引不满足最左前缀原则 索引idx_a_b_c(a,b,c);where b=xxx,跳过 a → 索引失效。
  2. like 以 % 开头 like '%张三',索引失效;like '张三%'可以走索引
  3. or 连接条件,一边有索引一边没有,整体索引失效
  4. 优化器判断数据量太大,索引不如全表,主动放弃索引(比如 where 条件匹配大部分数据)
  5. is not null、not in、!= 经常导致索引失效

排查方式:explain 看 type 是否变成 ALL,key 是否为 null。

六、慢 SQL 优化通用步骤总结

  1. 通过慢查询日志捕获慢 SQL
  2. explain 分析执行计划,看 type、key、rows、Extra
  3. 判断是否缺少索引,添加合适索引(优先联合索引、覆盖索引)
  4. 检查索引失效,改写 SQL,避免函数、隐式转换、like 前缀 %
  5. 出现 Using filesort/Using temporary,优化 order by、group by,增加排序字段到联合索引
  6. 尽量 select 不要用select *,按需查询字段,利用覆盖索引减少回表
  7. 大分页优化 limit offset,offset 很大时,用主键过滤where id>xxx limit 10

七、高频问答

Q1:explain 能看到 SQL 真实执行耗时吗?

A:不能,explain 只是预估执行计划,不会真正执行 SQL,只能看执行策略,不能看真实耗时。慢查询日志才记录真实耗时。
Q2:type 里 ref 和 range 区别?

A:ref 是索引等值匹配=;range 是范围查询> < between。ref 性能优于 range。
Q3:什么是覆盖索引,Extra 中 Using index 含义?

A:查询所需要的全部字段都在索引树中,不需要回表查询主键再查数据行。Extra 显示 Using index 代表命中覆盖索引,性能很好。
Q4:Using filesort 是什么?是不是一定文件磁盘排序?

A:不一定是磁盘文件,内存不够才落盘。本质:无法利用索引有序性,MySQL 需要额外进行排序操作,消耗 CPU,属于优化信号。
Q5:联合索引最左前缀原理?

A:联合索引 B + 树按照索引字段顺序排序,必须从最左边第一个字段开始连续匹配,跳过前面字段,后面字段无法使用索引。
Q6:in 查询一定走 range 吗?

A:不一定,in 匹配值过多,优化器会放弃索引,变成全表扫描。

结语

explain 是 MySQL 调优最核心工具,看懂执行计划,就能快速定位慢 SQL 瓶颈。 优化核心思路:尽量让 SQL 走索引,优先 ref/range;避免全表扫描、文件排序、临时表;优先设计覆盖索引减少回表。 结合慢查询日志定期巡检线上 SQL,提前发现性能隐患。

相关推荐
正儿八经的少年1 小时前
MyBatis 一对一、一对多 SQL
数据库·sql·mybatis
Elastic 中国社区官方博客1 小时前
Elastic 在《Forrester Wave™:2026 年第四季度智能运维平台》报告中被评为强劲表现者
大数据·运维·数据库·elasticsearch·搜索引擎·全文检索
Apache IoTDB1 小时前
从 IoTDB 到 CED-DB:哈尔滨工业大学探索云边端协同时序数据库
数据库·时序数据库·iotdb
闲云野鹤在人间1 小时前
头秃修炼手册:StorageClass+StatefulSet+ETCD 全套渡劫笔记
linux·数据库·笔记·云原生·kubernetes·etcd
ChaITSimpleLove1 小时前
Layout.WatchDog.NET 1.5.0-beta.2 发布:.NET 实时应用监控再升级,新增资源监控看板与 SQLite 支持
mysql·mongodb·pgsql·mssql·litedb\sqlite·.net轻量级日志记录器·node资源监控看板
Decay__2 小时前
03_MySQL 存储引擎与存储管理 —— InnoDB 深入、表空间/ibd、binlog/redo/undo 与双写缓冲
运维·数据库·sql·mysql·架构
瀚高PG实验室2 小时前
瀚高数据库提交数据后,是否需要COMMIT
数据库·postgresql·瀚高数据库
bksczm2 小时前
MySQL程序架构
数据库·mysql·架构
论文复现现场2 小时前
2026 低成本 AI Agent 模型怎么选?DeepSeek V4.1 Flash、Qwen3.8 Flash、GLM-5.3 Flash 对比
数据库·人工智能·qwen·ai agent·glm·deepseek