共计 4408 个字符,预计需要花费 12 分钟才能阅读完成。
MySQL 执行计划是 sql 语句经过查询优化器后,查询优化器会根据用户的 sql 语句所包含的字段和内容数量等统计信息,选择出一个执行效率最优(MySQL 系统认为最优)的执行计划,然后根据执行计划,调用存储引擎提供的接口,获取数据。
但是,在 MySQL 执行的时候,到底使用了一个什么样的执行计划,有没有用到索引。当数据规模比较大的时候,sql 执行的时候,执行计划不同,会直接影响 sql 的执行速度。这个时候,就需要对 sql 语句执行进行调试。
MySQL 我们在调试 sql 语句的时候,不会像我们写 Java 或者其他语言代码那样通过打断点的方式进行代码调试。这个时候,我们就需要通过查看执行计划来调试我们的 sql 了。MySQL 通过 EXPLAIN 来查看执行计划,我们写 sql 语句的时候,在语句之前加一个 EXPLAIN 就可以了。EXPLAIN 可以用在 SELECT、DELETE、INSERT、PEPLACE 以及 UPDATE 等语句中,如:
EXPLAIN SELECT
*
FROM
school
WHERE
school_nick = ‘ 县第一小学 ’
EXPLAIN SQL
执行后,结果如下:
这里介绍下每个字段以及每个字段的含义:
id
id 是执行计划的标识符,是 SELECT 查询的序号。如果结果集会跟其他表的结果用 UNION 关键字相结合,那么 id 可能为空。
id 是否为空,对执行计划的影响不大。
select_type
select_type 表示 sql 语句查询的类型。具体表示如下表:
select_type 的值 | 含义 |
SIMPLE | 简单的 select 查询,没有使用关联和子查询。 |
PRIMARY | 最外层 select,包含子查询的时候,最外层的查询 |
UNION | 在一个 UNION 查询中,第二次或以后的子查询操作 |
DEPENDENT UNION | 在一个 UNION 查询中,第二次子查询或以后的 SELECT 查询的时候需要依赖外部的查询 |
UNION RESULT | UNION 的返回结果集 |
SUBQUERY | 子查询语句的第一个 select 语句 |
DEPENDENT SUBQUERY | 依赖外部查询的第一个子查询 |
DERIVED | 派生表——该临时表是从子查询派生出来的,位于 form 中的子查询 |
MATERIALIZED | 物化子查询(不确定啥意思,以后研究后再回来补充,或者大神指教) |
UNCACHEABLE SUBQUERY | 无法缓存结果的子查询,必须为外部查询的每一行重新计算 |
UNCACHEABLE UNION | UNION 中的第二个或以后的不可缓存的子查询。 |
table
输出行引用的表的名称。一般为表格名称或别名,也可能为如下值:
1.UNION 的并集结果集。
2.derivedN 当前行指向派生结果集。可能是一个派生表,例如来自 FROM 子句的结果集。
3.subqueryN 当前行指向一个子查询的结果集。
type
连接类型。该列输出表示如何连接表。下面的类型表示从最好的到最坏的类型
1.system 该表只有一行(= 系统表)。这是 const 连接类型的特例。
2.const 最多只有一行匹配,在查询开始的时候,计算出常量对应的地址,直接访问,例如:select * from test where name =’zhang’ 当 name 是唯一索引的时候,就有可能出现 const。const 非常快,因为它只读一次。
3.eq_ref 除了 system 和 const 类型之外,这是最好的连接类型。当两个表联查时使用索引的所有部分(针对的是组合索引),且索引是 主键或唯一索引时使用它。使用“=”运算符来进行索引列的比较。
4.ref 非唯一索引扫描,返回某个匹配值的所有行。常用语非唯一索引。这里对于 eq_ref 和 ref 不熟悉的同学,可以看以下代码:
— 给 test 表的 name 字段加唯一索引,test2 的 job 行添加非唯一索引。
— 这个代码执行后,首先执行 test2 的查询,查出 job =‘teacher’的所有集合。
— 所以 test2 的 typ 是 ref 表示的是匹配 job =‘teacher’的一个结果集。
— 然后从结果集中取出 name 的集合,去匹配 test1.name 的结果。因为 test1.name 是唯一索引,所以一个 name 最多匹配到一条记录,所以 test 的 type 是 ref
EXPLAIN SELECT
*
FROM
test,
test2
WHERE
test. NAME = test2. NAME
AND test2.job = ’33’
test ref and eq_ref
执行结果如下图:
5.fulltext 使用 fulltext 索引进行查询。
6.ref_or_null 这种链接类型类似于 ref,但是,除了 ref 之外,还对包含 null 的值进行了搜索。常用于解析子查询。代码示例如下:
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;
7.index_merge 这个链接类型表示使用索引合并优化。输出内容包含在索引列表中。
8.unique_subquery 索引查找,替换子查询,以提高效率。
value IN (SELECT primary_key FROM single_table WHERE some_expr)
9. index_subquery 类似于 unique_subquery 但是替换 in 子查询,适用于非唯一索引,代码:value IN (SELECT key_column FROM single_table WHERE some_expr)
10.range 扫描部分索引,对索引的扫描从某一点开始,返回的是某个索引区域的值。常见的有基于索引的 < ,> 等的查询。
11. index 扫描全部索引,对索引进行整体扫描。
12.all 全表扫描,最慢的查询。应该避免
possible_keys
可能使用的 key,指出当前查询涉及到的行都含有那些索引。如果有索引就会列出,但是不一定会被使用。
key
实际使用的索引。如果没有使用索引,显示 null。
key_len
表中对应的索引最大可能长度。可以通过设置索引长度改变该值。例如:一个 varchar(255)的索引长度为 255,可是我们使用不到那么长,我们可以取字符串的前五位作为索引。这时 key_len 就是 5. 这里关于索引值的长度的选取规则,以后有机会再写一篇博客详细介绍。
ref
哪些字段和 key 一起被使用。没用过。
rows
受影响的行数。不是特别精确的。
Extra
解释额外的信息。包含 mysql 对于 query 优化的时候的一些附加信息。非常有用。可能出现的结果如下:
- const row not found 该表为空
- Deleting all rows 表格内数据被标记删除,正在删除中(某些存储引擎支持一种方法,以简单快捷的方式删除所有行,这时查询就会出现这个提示)
- Distinct MySQL 正在寻找不同的值,因此它在找到第一个匹配行后停止为当前行组合搜索更多行。
- FirstMatch(tbl_name) 半连接 FirstMatch 连接快捷方式策略用于 tbl_name。
- Full scan on NULL key 当优化程序无法使用索引查找访问方法时,子查询优化将作为回退策略发生。
- Impossible HAVING 该 HAVING 子句始终为 false,无法选择任何行。
- Impossible WHERE 该 WHERE 子句始终为 false,无法选择任何行。
- Impossible WHERE noticed after reading const tables MySQL 已经读取了所有 const(和 system)表,并注意到该 WHERE 子句始终为 false。
- No matching min/max row 没有行满足查询的条件的行
- no matching row in const table 对于具有连接的查询,有一个空表或没有满足唯一索引条件的行的表。
- No matching rows after partition pruning 对于 DELETE 或 UPDATE,优化器在分区修剪后发现没有删除或更新的内容。
- No tables used 查询没有 FROM 子句
- Not exists 查询的内容不存在
- Plan isn’t ready yet 优化程序尚未完成为在命名连接中执行的语句创建执行计划时,会出现此值。
- Range checked for each record MySQL 发现没有好的索引可以使用,但发现在前面的表的列值可能会使用某些索引。
- Recursive 递归
- Skip_open_table,Open_frm_only,Open_full_table
- Skip_open_table:表文件不需要打开。该信息已从数据字典中获得。
- Open_frm_only:只需要读取表信息的数据字典。
- Open_full_table:未优化的信息查找。必须从数据字典中读取表信息并读取表文件。
- unique row not found 对于查询,没有行满足 索引或表的条件。
- Using filesort 使用文件排序。MySQL 必须执行额外的传递以找出如何按排序顺序检索行。排序是通过根据连接类型遍历所有行并将排序键和指针存储到与该 WHERE 子句匹配的所有行的行来完成的。然后对键进行排序,并按排序顺序检索行
- Using index 仅使用索引树中的信息从表中检索列信息,而不必另外寻找读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。
- Using index condition 通过首先访问索引,确定是否可以读取完整的表行。
- Using index for group-by 使用索引分组。表示 MySQL 找到了一个索引,可用于检索 GROUP BY 或 DISTINCT 查询的所有列,而无需对实际表进行任何额外的磁盘访问。此外,索引以最有效的方式使用,因此对于每个组,只读取少数索引条目。
- Using index for skip scan 使用索引跳过扫描范围
- Using join buffer 将表数据读入缓存,然后从缓存中读数据来执行操作。
- Using MRR 使用多范围读取优化策略读取表。
- Using temporary 使用临时表,MySQL 需要创建一个临时表来保存结果。如果查询包含以不同方式列出列的 GROUP BY 和 ORDER BY 子句,则通常会发生这种情况。
- Using where 使用上了 where 限制,表示 MySQL 服务器在存储引擎受到记录后进行“后过滤”(Post-filter),如果查询未能使用索引,Using where 的作用只是提醒我们 MySQL 将用 where 子句来过滤结果集。
- Zero limit 查询有一个 LIMIT 0 子句,不能选择任何行。
- Only index 这意味着信息只用索引树中的信息检索出的,这比扫描整个表要快。
: