• 面试官:MySQL 执行计划都有哪些属性?分别是什么含义?

面试官:MySQL 执行计划都有哪些属性?分别是什么含义?

2025-08-16 12:33:39 栏目:宝塔面板 0 阅读

大家好,我是君哥。

使用 MySQL 时,我们常常通过执行计划来判断 SQL 语句的执行效率。那 MySQL 执行计划有哪些属性,分别代表什么含义呢?今天来聊一下这个话题。

通过 EXPLAIN 或 EXPLAIN FORMAT=JSON 查看 MySQL 执行计划时,会看到执行计划里面有 12 个属性,这些属性展示了优化器执行 SQL 的详细信息,对分析和优化 SQL 性能非常有帮助。如下面的思维导图。

图片

为了给属性增加说明,我们建两张表,表结构完全一样:

CREATE TABLE`test1` (
`id`INT(8) NOTNULL AUTO_INCREMENT,
`a`VARCHAR(10) COLLATE utf8_bin DEFAULTNULL,
`b`VARCHAR(10) COLLATE utf8_bin DEFAULTNULL,
`c`VARCHAR(10) COLLATE utf8_bin DEFAULTNULL,
  PRIMARY KEY (`id`),
KEY`idx_a` (`a`)
) ENGINE=INNODB AUTO_INCREMENT=5DEFAULTCHARSET=utf8 COLLATE=utf8_bin

CREATETABLE test2 LIKE test1;

用存储过程插入一些数据:

BEGIN 
 DECLARE i INT; 
SET i=1; 
WHILE(i<=1000)DO
INSERT INTO test1 VALUES(i, i, i, i); 
SET i=i+1; 
END WHILE;

INSERT INTO test2 SELECT * FROM test1;

执行计划

id

id 是 SQL 语句执行的顺序标识。

1. 如果 id 是相同的,表示子查询属于相同层级,这些子查询会依次被执行,比如 JOIN 语句:

EXPLAIN SELECT t1.*,t2.c AS c FROM test1 t1 JOIN test2 t2 ON t1.id = t2.id WHERE t1.id = 1 ;

图片

2. 如果 id 不同,表示这些 SELECT 属于不同层级,id 值越大,执行优先级越高。比如子查询或嵌套子查询 id 值通常大于外层查询的 id。

EXPLAIN SELECT * FROM test1 t1 WHERE EXISTS(SELECT id FROM test2 t2 WHERE t2.id > t1.id);

图片

3. id 也可能为 NULL,表示聚合查询,比如 UNION 语句。

EXPLAIN SELECT * FROM test1 t1 UNION SELECT * FROM test2;

图片

select_type

select_type 表示查询类型,常见的 select_type 如下:

  • SIMPLE: 简单查询,不包括子查询、聚合查询等。
  • PRIMARY: 当 SQL 包含子查询或者嵌套查询时,PRIMARY 表示最外层查询。
  • SUBQUERY: 出现在 SELECT 列表或 WHERE 子句中的非相关子查询(不依赖外层查询结果)。
EXPLAIN SELECT * FROM test1 t1 WHERE t1.id =(SELECT id FROM test2 t2 WHERE t2.a=100)

图片

  • DEPENDENT SUBQUERY: 出现在 SELECT 列表或 WHERE 子句中的相关子查询(依赖外层查询结果)。

执行流程如下:

a. 外部查询获取一行数据;

b. 将该行数据相关字段值传递给子查询;

c. 执行子查询获取结果;

d. 根据子查询结果决定是否返回当前行。


这类查询语句可能有性能问题,尤其是在数据量大的场景下。

EXPLAIN SELECT * FROM test1 t1 WHERE EXISTS(SELECT id FROM test2 t2 WHERE t2.id > t1.id);

图片

  • DERIVED: 派生表,指子查询产生的临时表,性能较差。
  • MATERIALIZED: 优化器选择将子查询结果生成一个临时表(通常用于 IN 子查询优化)。
  • UNION: UNION 操作中第二个及以后的 SELECT 语句。
EXPLAIN SELECT * FROM test1 t1 WHERE a=1 UNION SELECT * FROM test2 WHERE a=2 UNION SELECT * FROM test2 WHERE a=3;

图片

  • UNION RESULT: UNION 语句的结果集,id 为 NULL,见上面 SQL 执行计划。
  • DEPENDENT UNION: UNION 中的第二个及以后 SELECT 是相关子查询(依赖外层查询)。
EXPLAIN SELECT * FROM test1 WHERE id IN (SELECT id FROM test2 WHERE id=3 UNION ALL SELECT id FROM test2 WHERE a = 2)

图片

  • UNCACHEABLE SUBQUERY: 结果集不能被缓存的子查询,必须重新为外层查询的每一行进行评估,出现极少。(可能包含非确定性函数如 RAND(), NOW())。
EXPLAIN SELECT * FROM test1 t1 WHERE t1.id =(SELECT id FROM test2 t2 WHERE c=RAND())

table

表示当前查询正在访问的是哪个表,可以是正式表,也可以是派生表、物化子查询或者 UNION 结果。

EXPLAIN SELECT * FROM test1 t1 UNION SELECT * FROM test2;

图片

partitions

如果查询涉及分区表,表示查询将要访问的分区表。如果值为 NULL,表示未使用分区表。

type

type 是 MySQL 执行计划中最重要的属性,表示 SQL 执行所需要的访问方式,性能从优到差大致排序如下:

  • system: 表中只有一行数据,一般是系统表,是 const 的一个特例。
  • const: 通过主键或唯一索引进行等值查询,最多只返回一行记录。优化器将其视为常量,性能非常好。
EXPLAIN SELECT * FROM test1 WHERE id=1

图片

  • eq_ref: 在 JOIN 查询中,对于来自前表的每一行组合,从本表中使用主键或唯一非空索引进行等值匹配查找恰好找到一行记录,常见于使用主键或唯一索引进行等值连接,性能非常好。
EXPLAIN SELECT t1.* FROM test1 t1 LEFT JOIN test2 t2 ON t2.id = t1.id

图片

  • ref: 使用非唯一索引进行等值查询,可能返回多行匹配记录,或者在 JOIN 中使用非唯一索引进行部分前缀匹配。
EXPLAIN SELECT t1.*,t2.c AS c FROM test1 t1 JOIN test2 t2 ON t1.a = t2.a;

  • fulltext: 使用 FULLTEXT 索引执行查找。通常与 MATCH()、AGAINST() 语句一起使用来进行全文搜索。
ALTER TABLE test1 ADD FULLTEXT (c);
EXPLAIN SELECT * FROM test1 WHERE MATCH(c) AGAINST('keyword');

图片

  • ref_or_null: 跟 ref 相似,不同的是 SQL 条件中包含 NULL 列的数据。
EXPLAIN SELECT * FROM test1 WHERE a='10' OR a = NULL;

  • index_merge: 表示优化器使用了索引合并优化(Index Merge Optimization),即对同一个表使用了多个索引进行扫描,然后将扫描结果合并,如果使用了 index_merge,Extra 属性会出现 Using union/Using sort_union/Using intersect。这个 type 不一定最高效。
EXPLAIN SELECT * FROM test1 WHERE a='900' OR id > 950;

图片

  • unique_subquery: 在某些 IN 子查询中用来替换 eq_ref,效率更高。
EXPLAIN SELECT * FROM test1 t1 WHERE t1.id IN (SELECT t2.id FROM test2 t2 WHERE t1.a = t2.a) OR t1.a='100';

图片

  • index_subquery: 类似于 unique_subquery,但适用于使用非唯一索引的 IN 子查询。
  • range: 使用索引进行范围查询。WHERE 子句中可以包含 BETWEEN, >, <, >=, <=, IN(), LIKE 'prefix%'等范围查询操作符。range 类型比全表扫描效率高,但需注意范围大小。
EXPLAIN SELECT * FROM test1 WHERE a > '1' AND a < '20';

  • index: 全索引扫描(Index Scan)。通常发生在使用覆盖索引或者按照索引顺序进行全表扫描。因为只扫描索引树,不读取文件,性能比全表扫描(ALL)快。
EXPLAIN SELECT a, b FROM test1 WHERE a > '10';

图片

  • ALL: 全表扫描,性能较差,一种情况是优化器找不到合适的索引,另一种情况是表数据量很小,优化器认为全表扫描更快。

possible_keys

优化器可以考虑使用的索引列表,SQL 语句可能涉及索引都可能被列出。但是优化器最终不一定会选择哪个索引,如果这个属性为 NULL,表示没有可用的索引,需要考虑建索引进行优化。

key

优化器实际决定使用的索引。如果为 NULL,表示优化器决定不使用任何索引,需要全表扫描,一方面可能因为表小,使用全表扫描也很快,另一方面也可能是因为没有合适的索引,优化器认为全部扫描效率更高。

比如 possible_keys 属性中有索引,但是优化器认为使用该索引不如全表扫描效率高。

如果属性值是 PRIMARY 则表示使用了主键索引。

key_len

优化器选择的索引中使用到的字段长度(单位:字节)。

以文章开头的 SQL 为例,给 a、b 字段加了联合索引,下面 SQL 的执行计划看一下:

EXPLAIN SELECT a, b FROM test1;

图片

那这个 66 是怎么来的呢?

varchr(N)变长字段且允许 NULL,索引长度 = N * 字符集长度参数 + 1(字段可以为 NULL) + 2(变长字段) = 10 * 3 + 1 + 2 = 33,a + b 联合索引长度就是 66.


字符集长度参数:utf8mb4 为 4 字节,utf8 = 3,gbk = 2,latin1 = 1

这个属性可以判断联合索引被使用了前几个字段,值越小表示使用的索引部分越少。

ref

显示 key 属性指定的索引中,查找值所用到的列或常量(即与索引进行比较的内容)。

常见值说明:

  • const:常量值。
EXPLAIN SELECT * FROM test1 WHERE id = '5';

图片

  • 列名,表示使用到另一个表的列,如 JOIN 语句。
EXPLAIN SELECT t1.* FROM test1 t1 LEFT JOIN test2 t2 ON t2.id = t1.id

图片

  • func:表示 SQL 中使用了函数结果。
  • NULL:通常出现在 type 属性是 index(全索引扫描)或 ALL(全表扫描)类型中。

rows

优化器估算的执行该语句需要扫描的行数。只是一个估算值,不是精确值。

rows 基于表统计信息(SHOW TABLE STATUS)和索引统计信息,优化器可能根据这个值选择执行计划。

rows 数值越大,意味着需要处理的 I/O 和 CPU 越多,性能越差,因此 rows 属性值是识别性能瓶颈的重要指标。

filtered

使用 WHERE 子句中的过滤条件后,返回的数据占总数据的百分比,数值范围 0.00 ~ 100。

filtered 值越低,表示 WHERE 条件过滤效果越好。

Extra

SQL 执行的额外信息,提供了优化器的处理细节。常见的值如下:

  • Using index: 使用覆盖索引,查询的列完全包含在所使用的索引的列中,无需回表访问数据行,性能好。
  • Using where: 存储引擎返回数据行后,服务器层需要应用额外的 WHERE 条件进行过滤,如果 rows 很大,则服务器过滤会花不少时间。
  • Using temporary: 需要使用临时表,常见包括 GROUP BY, DISTINCT, UNION, ORDER BY 的 SQL 语句,可以考虑使用索引进行优化,比如让 ORDER BY 和 GROUP BY 字段走上索引。
  • Using filesort: 需要走非索引的排序,如果内存不够,需要在磁盘进行排序。尝试 ORDER BY 和 GROUP BY 字段走上索引排序。
  • Using index condition: 索引下推,减少回表次数,可以考虑作为性能优化的手段。
  • Select tables optimized away: 优化器确定查询可以只从索引中获取结果(例如 MIN(key_column), MAX(key_column), COUNT(*) 通过索引覆盖),甚至不需要访问表或索引数据(直接使用元数据统计),性能非常好。
  • Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access),Using join buffer (hash join): 表示 JOIN 操作使用了 Join Buffer 。
  • Impossible WHERE: WHERE 子句的条件始终为 false,比如 WHERE 1=0,不会返回数据。
  • Start temporary, End temporary: 用于半连接(semijoin)物化去重策略,可以优化 IN/EXISTS。
  • Distinct: 用于 DISTINCT 或 UNIQUE 子查询,找到第一个后停止继续查找。
  • Range checked for each record: 没有好的索引可以使用,但是发现在知道前表字段值的情况下后表可能用上某些索引。对于来自前表的每一行,MySQL 都会在后表中通过范围查询(range)或索引合并(index_merge)进行数据查询。性能不太好,但比完全不走索引的 join 语句好一些。
EXPLAIN SELECT * FROM test1 t1 WHERE EXISTS(SELECT id FROM test2 t2 WHERE t2.id > t1.id);

图片

总结

本文对 MySQL 执行计划中的属性进行了详细介绍,使用执行计划优化 SQL 时,关注下面几点:

  • 查看 type 避免全表扫描 ALL 和全索引扫描 index,尽量能使用 const, eq_ref, ref, range。
  • 查看 key 是否使用了合适的索引。
  • 查看 rows 是否过大,结合 filtered 估算最终结果集大小。
  • 查看 Extra,避免使用 Using temporary 和 Using filesort,尽量使用 Using index(覆盖索引)。通过 Using where 过滤结果集,通过 Using index condition 的减少回表次数。
  • 通过 key_len 查看联合索引使用情况。
  • 查看 select_type,如果有 DEPENDENT SUBQUERY 或 DERIVED,要考虑进行优化。

本文地址:https://www.yitenyun.com/344.html

搜索文章

Tags

数据库 API FastAPI Calcite 电商系统 MySQL Web 应用 异步数据库 数据同步 ACK 双主架构 循环复制 Deepseek 宝塔面板 Linux宝塔 Docker 生命周期 JumpServer JumpServer安装 堡垒机安装 Linux安装JumpServer 序列 核心机制 esxi esxi6 root密码不对 无法登录 web无法登录 SSL 堡垒机 跳板机 HTTPS Windows Windows server net3.5 .NET 安装出错 宝塔面板打不开 宝塔面板无法访问 查看硬件 Linux查看硬件 Linux查看CPU Linux查看内存 连接控制 机制 HTTPS加密 Windows宝塔 Mysql重置密码 无法访问宝塔面板 HexHub 运维 Oracle 处理机制 Serverless 无服务器 语言 Spring Redis 异步化 ES 协同 技术 group by 索引 InnoDB 数据库锁 监控 开源 PostgreSQL 存储引擎 分页查询 高可用 SQL 动态查询 响应模型 缓存方案 缓存架构 缓存穿透 R edis 线程 数据 主库 自定义序列化 服务器 管理口 scp Linux的scp怎么用 scp上传 scp下载 scp命令 日志文件 MIXED 3 查询 SVM Embedding 存储 OB 单机版 Linux 安全 工具 电商 系统 SQLark SQLite-Web SQLite 数据库管理工具 架构 Rsync Postgres OTel Iceberg RocketMQ 长轮询 配置 Recursive 云原生 流量 共享锁 • 索引 • 数据库 防火墙 黑客 修改DNS Centos7如何修改DNS 向量数据库 大模型 聚簇 非聚簇 sftp 服务器 参数 PG DBA 缓存 业务 AI 助手 Ftp ​Redis 机器学习 推荐模型 场景 GreatSQL 连接数 优化 万能公式 Netstat Linux 服务器 端口 数据备份 MySQL 9.3 openHalo 数据分类 加密 同城 双活 信息化 智能运维 RDB AOF INSERT COMPACT 窗口 函数 人工智能 推荐系统 核心架构 订阅机制 事务 Java 开发 MVCC 磁盘架构 线上 库存 预扣 redo log 重做日志 网络架构 网络配置 Doris SeaTunnel prometheus Alert Redisson 锁芯 向量库 Milvus mini-redis INCR指令 Undo Log 高效统计 今天这篇文章就跟大家 Python 引擎 性能 网络故障 B+Tree ID 字段 模型 IT运维 Canal Web PostGIS Redis 8.0 不宕机 数据脱敏 加密算法 R2DBC Hash 字段 分布式 集中式 崖山 新版本 自动重启 Pottery ZODB 微软 SQL Server AI功能 容器化 虚拟服务器 虚拟机 内存 OAuth2 Token JOIN Entity DBMS 管理系统 MongoDB 容器 数据类型 数据结构 读写 sqlmock 启动故障 悲观锁 乐观锁 分库 分表 LRU SpringAI StarRocks 数据仓库 数据集成工具 分页 单点故障 QPS 高并发 传统数据库 向量化 数据页 工具链 Testcloud 云端自动化 Redka 排行榜 排序 发件箱模式 事务隔离 部署 聚簇索引 非聚簇索引 分页方案 排版 filelock 大表 业务场景 意向锁 记录锁 速度 服务器中毒 Web 接口 1 SSH 池化技术 连接池 Caffeine CP 仪表盘 分布式架构 分布式锁​ dbt 数据转换工具 日志 原子性 EasyExcel MySQL8 AIOPS MCP 开放协议 优化器 网络 InfluxDB Order IT 双引擎 RAG HelixDB 频繁 Codis Go 数据库迁移 字典 单线程 Ansible 对象 Crash 代码 LLM 事务同步 订单 线程安全 Pump List 类型 UUIDv7 主键 锁机制 语句 服务器性能 TIME_WAIT 负载均衡 行业 趋势 MGR 分布式集群 分布式锁 Zookeeper 解锁 调优 并发控制 恢复机制 ReadView 国产数据库 闪回 产业链 兼容性 Next-Key 国产 用户 快照读 当前读 视图 RR 互联网 关系数据库 编程 数据字典 算法 主从复制 代理 GitHub Git count(*) count(主键) 行数 播客 UUID ID 神经系统 慢SQL优化 千万级 表空间 拦截器 动态代理 Valkey Valkey8.0 矢量存储 数据库类型 AI代理 查询规划 恢复数据 CAS 失效 多线程 技巧 Weaviate