MySQL中SQL执行计划详解
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 这意味着信息只用索引树中的信息检索出的,这比扫描整个表要快。