MySQL执行计划详解:优化查询性能的关键
1、table
EXPLAIN中语句输出的每条记录都对应着某个单表的访问方法,table列记录着该表的表名。
示例1:
EXPLAIN SELECT * FROM s1;

示例2:
EXPLAIN SELECT * FROM s1 INNER JOIN s2;

2、id
可以认为在查询语句中SELECT出现几次就有几个不同的id,这与查询语句的复杂程度无关。
示例1:
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';

示例2:多表查询
EXPLAIN SELECT * FROM s1 INNER JOIN s2;

示例3:
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';

示例4:优化器也许会对子查询的查询语句进行重写,转换为多表查询
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field = 'a');
示例5:UNION去重
EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2; # 要去重,因此有一张临时表,总共三张表
EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2; # 不去重,仍然是两张表
总结:
- id相同则表示为同一组,由上往下顺序执行;
- 所有结果中,id的值越大,优先级越高,越先要执行;
- 一个SQL查询的趟数越少越好。
3、select_type
每一个SELECT关键字代表的小查询都会定义一个select_type属性,由此就可以知道这个小查询在整体查询中扮演的角色。
示例1:
EXPLAIN SELECT * FROM s1; # simple
EXPLAIN SELECT * FROM s1 INNER JOIN s2; # simple
示例2:对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION,最左边的那个查询select_type值就是PRIMARY,临时表的是UNION RESULT。
EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;

示例3:子查询(不能转换为多表查询并且该子查询是不相关子查询)
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';

子查询不能转换为多表查询但是他是相关子查询时,内查询类型是DEPENDENT SUBQUERY
EXPLAIN SELECT * FROM s1
WHERE key1 IN (SELECT key1 FROM s2 WHERE s1.key2 = s2.key2) OR key3 = 'a';

示例4:对于包含“派生表”的查询
EXPLAIN SELECT *
FROM (SELECT key1, COUNT(*) AS c FROM s1 GROUP BY key1) AS derived_s1
WHERE c > 1;

示例5:子查询被转换为了物化表
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2)

4、partitions
不涉及分区的话该类型就是NULL。
5、type⭐
执行计划的一条记录就代表MySQL对某张表的查询时的访问方法(访问类型)。
完整的访问方法如下: system , const , eq_ref , ref , fulltext , ref_or_null , index_merge , unique_subquery , index_subquery , range , index , ALL 。
示例1:当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的(内置一个变量会统计数量),比如MyISAM、Memory,那么对该表的访问方法就是system。
CREATE TABLE t
(i INT)
ENGINE=MYISAM;
INSERT INTO t VALUES(1);
EXPLAIN SELECT * FROM t;

示例2:根据主键(id)或者唯一二级索引(key2)列与常数进行等值匹配时,对单表的访问方法就是const
EXPLAIN SELECT * FROM s1 WHERE id = 10005; # const
EXPLAIN SELECT * FROM s1 WHERE key2 = 10066; # const
示例3: 在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的则对该被驱动表的访问方法就是eq_ref`
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;

示例4:通过普通的二级索引列与常量进行等值匹配(要注意与字段类型一致)时来查询某个表,那么对该表的访问方法就可能是ref
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';

在上一个基础上索引列的值也可以是`NULL`值时,那么对该表的访问方法就可能是ref_or_null
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key1 IS NULL; # ref_or_null
示例5:单表访问方法时在某些场景下可以使用Intersection、Union、Sort-Union这三种索引合并的方式来执行查询
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';

示例6:unique_subquery是针对在一些包含IN子查询的查询语句中,如果查询优化器决定将IN子查询转换为EXISTS子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划的type列的值就是unique_subquery
EXPLAIN SELECT * FROM s1
WHERE key2 IN (SELECT id FROM s2 WHERE s1.key1 = s2.key1) OR key3 = 'a';

示例7:如果使用索引获取某些范围区间的记录,那么就可能使用到range访问方法
EXPLAIN SELECT * FROM s1 WHERE key1 IN ('a', 'b', 'c');

示例8:当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是index
EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'a';

示例9:全表扫描
EXPLAIN SELECT * FROM s1;

6、possible_keys与key
possible_keys表明对某个表执行单表查询时可能用到的索引。一般查询涉及到的字段如果存在索引,则该索引将被列出,但不一定被查询使用。key列表示实际用到的索引有什么,如果为NULL,则表示没有使用索引。
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';

7、key_len
表示实际使用到的索引长度(字节数)。针对联合索引,判断是否充分利用了索引,并且值越大越好。
EXPLAIN SELECT * FROM s1 WHERE id = 10005; # 4
EXPLAIN SELECT * FROM s1 WHERE key2 = 10126; # 5
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a'; # 303
# 以下三条表示值越大越好,越充分利用索引
EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a'; # 303
EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part2 = 'b'; # 606
EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part2 = 'b' AND key_part3 = 'c'; # 909
EXPLAIN SELECT * FROM s1 WHERE key_part3 = 'a'; # NULL
8、ref
当使用索引列进行等值查询时,与索引列进行等值匹配的对象信息(例如是一个常数或者是某个列)
示例1:常数
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';

示例2:具体的列
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;

示例3:聚合函数
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s2.key1 = UPPER(s1.key1);

9、rows
表示预估的需要读取的记录条数(值越小越好)
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z';

10、filtered
表示某个表经过搜索条件过滤后剩余记录条数的百分比
示例1:
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND common_field = 'a';

示例2:更关注的是在连接查询中驱动表对应的执行计划记录的filtered值
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'a';

11、Extra
通过这些额外信息可以更准确的理解MySQL是如何执行给定的查询语句。
(1)No tables used
EXPLAIN SELECT 1;
(2)Impossible WHERE
EXPLAIN SELECT * FROM s1 WHERE 1 != 1;
(3)Using where
EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND common_field = 'a';
(4)No matching min/max row
EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'abcdefg';
(5)Using index
EXPLAIN SELECT key1,id FROM s1 WHERE key1 = 'a';
(6)Using index condition
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';
(7)Using where; Using join buffer (hash join)
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;
(8)Using where; Not exists
EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.id IS NULL;

(9)Using union(idx_key1,idx_key3); Using where
EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
(10)Zero limit
EXPLAIN SELECT * FROM s1 LIMIT 0;
(11)文件排序Using filesort
EXPLAIN SELECT * FROM s1 ORDER BY common_field LIMIT 10;
(12)Using temporary
EXPLAIN SELECT DISTINCT common_field FROM s1;
总结
- EXPLAIN不考虑各种cache,只针对SQL本身;
- EXPLAIN不能显示MySQL在执行查询时所做的优化工作;
- EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况;
- 部分统计信息是估算的,并非精确值。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)