一、执行计划输出的各列详解
1、table:
被分析执行计划的表名。
2、id:
查询执行计划的标识符,用于表示查询中每个 SELECT 子句的执行顺序和层级关系。
具体含义如下:
- 相同 id 值
- 表示同一层级:若多个行具有相同的 id,则它们属于同一个 SELECT(如 JOIN 多表或子查询在同一层级)。
- 执行顺序:从上到下按顺序执行(由 EXPLAIN 输出行的顺序决定)。
- 不同 id 值
- 嵌套层级关系:
- id 值越大,优先级越高,越先执行。
- 例如:内层子查询的 id 比外层查询的 id 值大,因此内层子查询先于外层执行。
- 表示不同子查询:每个 SELECT(包括子查询、派生表等)会有独立的 id。
- 嵌套层级关系:
- 特殊值 NULL
- 表示是聚合结果集的行,常见于:
- UNION RESULT:合并 UNION 操作的最终结果。
- 某些优化器生成的聚合步骤(如使用临时表处理派生表)。
- 表示是聚合结果集的行,常见于:
3、select_type:
用于表示查询中每个 SELECT 语句的类型,它揭示了查询结构(如简单查询、子查询、UNION 操作等)的执行方式。以下是常见取值及其详细解释:
- SIMPLE:查询语句中不包含UNION或者子查询的查询都算作是SIMPLE类型,是最简单的 SELECT 查询。
- PRIMARY:对于包含UNION、UNION ALL或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询的select_type值就是PRIMARY。
- UNION:对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION。
- UNION RESULT:MySQL选择使用临时表来完成UNION查询的去重工作,针对该临时表的查询的select_type就是UNION RESULT。
- SUBQUERY:
- 含义:非相关子查询(子查询可以独立运行,不依赖外层查询)。
- 位置:在 SELECT、WHERE 或 HAVING 子句中(不在 FROM 子句中)。
- DEPENDENT SUBQUERY
- 含义:相关子查询(依赖外层查询的结果)。
- 执行逻辑:外部查询每返回一行数据,子查询就执行一次。
- DERIVED
- 含义:派生表(临时表),来自 FROM 子句中的子查询。
- 优化器行为:MySQL 会先将子查询结果存入临时表,再执行外层查询。
- MATERIALIZED
- 含义:物化子查询(MySQL 将子查询结果存入临时表以优化性能)。
- 场景:常用于 IN 子查询的优化。
4、partitions:
分区,一般均为 null。
5、type:
表示对某个表执行查询时的访问方法,常见的类型如下(性能从好到差)
- system:当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory,那么对该表的访问方法就是system。
- const:根据主键或者唯一二级索引列与常数进行等值匹配时,通过索引一次就找到了。
- eq_ref:在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则对该被驱动表的访问方法就是eq_ref。
- ref:当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是ref。
- fulltext
- ref_or_null:当对普通二级索引进行等值匹配查询,该索引列的值也可以是NULL值时,那么对该表的访问方法就可能是ref_or_null。
- index_merge:表示将使用索引合并的方式来执行对表的查询。
- unique_subquery
- index_subquery
- range:索引范围扫描,例如使用IN、BETWEEN等。
- index:当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是index
- ALL:全表扫描。
6、possible_keys和key
7、key_len
8、ref
9、rows:
代表预计需要扫描的行数(估算值),这个值越小越好。
10、filtered:
表示存储引擎层返回的数据在server层经过where条件过滤后,剩余记录数的百分比(估算)。这个字段在MySQL 5.7之后才有,对于多表查询很有用。如果filtered的值很低,说明索引选择行后,还需要在server层过滤掉大量数据,可能需要优化索引(如覆盖索引)或调整查询条件。
11、extra:额外信息。
- No tables used:当查询语句的没有FROM子句时将会提示该额外信息。
- Impossible WHERE:查询语句的WHERE子句永远为FALSE时将会提示该额外信息。
- No matching min/max row:当查询列表处有MIN或者MAX聚集函数,但是并没有符合WHERE子句中的搜索条件的记录时,将会提示该额外信息。
- Using index:在可以使用索引覆盖的情况下,在Extra列将会提示该额外信息。
- Using index condition:在可以使用索引下推的情况下,在Extra列将会提示该额外信息。
- Using where:当某个搜索条件需要在server层进行判断时,在Extra列中会提示Using where。
- Using join buffer (Block Nested Loop):在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为其分配一块名叫join buffer的内存块来加快查询速度,此时在Extra列将会提示该额外信息。
- Using intersect(...)、Using union(...)和Using sort_union(...):如果执行计划的Extra列出现了Using intersect(...)提示,说明准备使用Intersect索引合并的方式执行查询,括号中的...表示需要进行索引合并的索引名称;如果出现了Using union(...)提示,说明准备使用Union索引合并的方式执行查询;出现了Using sort_union(...)提示,说明准备使用Sort-Union索引合并的方式执行查询。
- Zero limit:当我们的LIMIT子句的参数为0时,表示压根儿不打算从表中读出任何记录,将会提示该额外信息。
- Using filesort:如果某个查询需要使用文件排序的方式执行查询,就会在执行计划的Extra列中显示Using filesort提示。
- Using temporary:如果查询中使用到了内部的临时表,在执行计划的Extra列将会显示Using temporary提示。
注:在许多查询的执行过程中,MySQL可能会借助临时表来完成一些功能,比如去重、排序之类的,比如我们在执行许多包含DISTINCT、GROUP BY、UNION等子句的查询过程中,如果不能有效利用索引来完成查询,MySQL很有可能寻求通过建立内部的临时表来执行查询。
- Start temporary, End temporary:通过建立临时表来实现为外层查询中的记录进行去重操作时,驱动表查询执行计划的Extra列将显示Start temporary提示,被驱动表查询执行计划的Extra列将显示End temporary提示。
- LooseScan:在将In子查询转为semi-join时,如果采用的是LooseScan执行策略,则在驱动表执行计划的Extra列就是显示LooseScan提示。
- FirstMatch(tbl_name):在将In子查询转为semi-join时,如果采用的是FirstMatch执行策略,则在被驱动表执行计划的Extra列就会提示该额外信息。
二、面对 EXPLAIN 输出的那么多列中,如何聚焦关键信息,找到 SQL 优化点呢?
可着重分析以下关键列:
1. type(访问类型)
- 作用:表示 MySQL 查找数据的方式,性能从优到差排序:system > const > eq_ref > ref > range > index > ALL
- 重点关注:
- 避免 ALL(全表扫描):若无索引或索引失效,必须优化(如添加索引)。
- 目标至少达到 range:如 WHERE 使用范围查询(BETWEEN、>)。
- 理想状态 ref/eq_ref:索引等值查询(如 =)或 JOIN 优化。
2. key(实际使用的索引)
- 作用:显示查询实际命中的索引。若为 NULL 表示未用索引。
- 优化方向:
- 若 key 为 NULL:强制检查 WHERE/JOIN 条件是否可加索引。
- 对比 possible_keys(可能用到的索引):若实际未用,需分析原因(如索引选择性低)。
3. rows(预估扫描行数)
- 作用:MySQL 预估需要扫描的行数(非精确值)。
- 优化意义:
- 数值越大,性能越差。结合 type 判断:
- 若 type=ALL 且 rows 很大 → 急需优化索引。
- 即使 type=index,若 rows 过高(如百万级),仍需优化(考虑覆盖索引)。
- 数值越大,性能越差。结合 type 判断:
4. Extra(额外信息)
- 作用:揭示 SQL 执行的细节瓶颈。
- 关键值解析:
- Using where:存储引擎返回结果后,Server 层需再次过滤(检查索引是否充分利用)。
- Using temporary:需创建临时表处理(如 GROUP BY、DISTINCT 未用索引)。
优化:为 ORDER BY/GROUP BY 字段添加联合索引。
- Using filesort:需额外排序(内存/磁盘)。
优化:为 ORDER BY 字段添加索引。
- Using index:好的信号! 使用覆盖索引(无需回表)。
5. possible_keys(可能用到的索引)
- 作用:列出理论上可用的索引。
- 与 key 对比分析:
- 若有值但 key 为 NULL → 优化器未选择索引。
- 可能原因:索引统计信息过期(执行 ANALYZE TABLE)、索引选择性低。
- 若为空 → 无可用索引,需新建索引。
6. filtered(过滤比例) ★★(MySQL 5.7+)
- 作用:存储引擎返回数据后,剩余数据的有效占比(0~100%)。
- 示例:若 rows=1000 且 filtered=10% → 最终需处理 100 行。
- 优化:低百分比需检查 WHERE 条件是否有效利用索引。
实践建议
- 索引优化:确保 WHERE、JOIN、ORDER BY/GROUP BY 字段有合适索引。
- 避免全表扫描:紧盯 type 和 key,消灭 ALL。
- 关注执行成本:rows × filtered 代表实际处理量。
- 利用覆盖索引:减少回表(Extra: Using index)。
- 定期更新统计信息:ANALYZE TABLE 避免优化器误判索引。
通过聚焦这些核心字段,可快速定位 SQL 瓶颈并高效优化!