MySQL 执行计划使用详解

愿天堂没有BUG

共 6527字,需浏览 14分钟

 · 2021-11-04

执行计划是什么?

使用 EXPLAIN 关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是 如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈。

官网介绍:dev.mysql.com/doc/refman/…

前提介绍:文中所有案例 mysql 版本为 5.7.23

执行计划帮助我们完成什么事情?

  • 表的读取顺序

  • 数据读取操作的操作类型

  • 哪些索引可以使用

  • 哪些索引被实际使用

  • 表之间的引用

  • 每张表有多少行被优化器查询

怎么使用执行计划?

  • expain + SQL 语句

  • 执行计划包含信息

执行计划包含信息解释

id

select 查询的序列号,包含一组数字, 表示查询中执行 select 子句或操作表的顺序

use oemp;

#测试表1
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`other_column` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

#测试表2
CREATE TABLE `t2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`other_column` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

#测试表3
CREATE TABLE `t3` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`other_column` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

#id 相同
explain select t2.* from t1,t2,t3 where t1.id = t2.id and t1.id = t3.id
and t3.other_column = '';

#id 不同
explain select t2.* from t2 where id = (select id from t1 where id =
(select t3.id from t3 where t3.other_column = ''));

#id 相同和不同同时存在
explain select t2.* from (select t3.id from t3 where t3.other_column = '') s1,t2
where s1.id = t2.id;
复制代码

包含三种情况:id 相同,id 不同,id 相同和 id 不同同时存在。

id 相同

id 相同,执行结果从上而下

  • 运行结果

id 不同

id不同如果是自查询,id 的序号会递增,id 值越大,优先级越高,越先被执行

  • 运行结果

id 相同和 id 不同时存在

id 如果相同,可以认为是一组的,从上往下执行;在所有组中,id 值越大,优先级越高,越先被执行;衍生 = DERIVED

  • 执行结果

derived_merge 是 Mysql5.7 引入的,会试图将 Derived Table (派生表,from 后的自查询) 视图引用,公用表达式(Common table expressions) 与外层查询进行合并。MySQL 5.7 不在兼容的实现方式,可以通过调整 optimizer_switch 来加以规避

set optimizer_switch='derived_merge=off';
复制代码

说白了,如果设置为 on 那么就不会出现 derived_merge 行 结果如下:

select_type

包括范围:simple. primary,subquery, derived, union, union result 查询类型主要是用于区别普通查询,联合查询,子查询等复杂的查询

  • simple,简单的select 语句,查询中不包含自查询或者 union

  • primary, 查询若包含任何复杂的子部分,最外层查询则被标记为primary

  • subquery, 在 select 或 where 列表中包含子查询

  • derived,在 from 列表中包含自查询被标记为 derived (衍生)MySQL 会递归执行这些自查询,把结果放在临时表中。

  • union,若第二个 select 出现在 union 之后,则被标记为 union. 若 union 包含在 from 子句子查询中,外层 select 将别标记为 derived

  • union result, 从 union 表中获取结果的 select


table

  • 这行数据是关于那种表的


type

类型:all , index , range, ref, eq_ref, const, system ,null type 显示的是防卫类型,是较为重要的一个指标,结果从好到坏依次是:system > count > eq_ref > range > index > all

sytem > const > eq_ref > ref > fulltext > ref_or_null > index_merge >> unique_subquery > index_subquery > range > index > ALL

system

表只有一行记录(等于系统表),这是 const 类型的特列, 平时不会出现,这个也可以忽略不计

count

explain select * from (select * from t1 where id =1) d1;
复制代码

表示通过索引一次就找到了, const 用于比较 primary key 或者 unique 索引。因为只匹配一行数据,所以很快如将主键置于where 列表中, MySQL 就能将该查询转换为一个常量。

eq_ref

explain select * from t1, t2 where t1.id = t2.id;
复制代码

唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描. 查询案例:

ref

# tb_emp ddl
CREATE TABLE `tb_emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`dept_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
) ;

#员工表添加年龄列
alter table tb_emp add column `age` int(11) default null after `name`;

#添加复合索引
create index idx_emp_name_age on tb_emp(`name`, `age`);

explain select * from tb_emp where `name` = 'z3';
复制代码

非唯一性索引扫描, 返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独的行,然而,它可能会找到多个符合个条件的行,所以它应该属于查找和扫描的混合体

range

explain select * from t1 where id between 1 and 3;

explain select * from t1 where id in (1, 2, 3);
复制代码

只检索给定范围内的行,使用一个索引来选择行。key 列显示使用了哪个索引 一般就是你在 where 语句中出现了 between、<、>、in 等的查询 这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某个点,而结束于另一个点,不用全表扫描 案例结果:

index

explain select id from t1;
复制代码

Full Index Scan , index 于 ALL的却别 ,index 类型只遍历索引树, 这通常比 ALL 快, 因为索引文件通常比数据文件小。(也就是说虽然 all 和 index 都是读全表,但是index 是从索引中读取的, 而 all 是从硬盘中读取的 )查询结果:

all

explain select * from t1;
复制代码

Full Table Scan 将遍历全表找到匹配的行备注:一般来说,得以保证查询至少达到 rang 级别, 最好能达到 ref。

possible_keys

显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用.

key

实际使用的索引,如果为NULL,则没有使用索引 查询中若使用了覆盖索引,则该索引仅出现在KEY列表中

explain select col1, col2  from t1;

create index idx_col1_col2 on t1(col1, col2);

explain select col1, col2 from t1;
复制代码

案例一(加索引之前)案例二(加索引之后)

key_len

desc t1; 
explain select * from t1 where col1 = 'ab';
explain select * from t1 where col1 = 'ab' and col2 = 'bc';
复制代码

表示索引中使用的字节数,可通过该列计算查询中的使用的索引的长度,在不损失精确性的情况下,长度越短越好 key_len 显示的只为索引字段的最大可能长度,** 并非实际使用长度**。即 key_len e是更具表定义计算而得,不是通过表内检索出的。查询结果:总结:条件越多,付出的代价越大,key_len 的长度也就越大,建议在一定条件的情况下,key_len 越短,效率越高。

Rows

根据表统计信息及索引选用情况, 大致估算出找到所需的记录所需读取的行数

filtered

Extra

包含不适合其他列中显示但十分重要的额外信息 id, select_type, table, type , possible_keys, key, key_len, ref, rows, Extra

1. Using filesort

文件排序

2. Using temporary

explain select col2 from t1 where col1 in ('ab', 'ac', 'as') group by col2 \G;

explain select col2 from t1 where col1 in ('ab', 'ac', 'as')
group by col1, col2, col3 \G;
复制代码

使用了临时表保存中间结果, MySQL 在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by 。例子:

3. Using index

explain select col2 from  t1 where col1=100;

explain select col1, col2 from t1;
复制代码

表示相应的 select 操作使用了覆盖索引 (Covering Index), 避免了访问表的数据行,效率不错~ 如果同时出现 using where , 表示索引被用来执行索引键值的查找;如果没有同时出现 using where , 表明索引引用来读取数据而非执行查找动作。例子:覆盖索引 (Covering Index)

  • 覆盖索引 (Covering Index), 一说为索引覆盖

  • 理解方式一:就是 select 的数据列只用从索引中就能取得,不必读取数据行, MySQL 可以利用你索引返回 select 列表的字段, 而不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖

  • 理解方式二:索引是高效找到的行的一个方法, 但是一般数据库也能使用索引找到一个列的数据, 因此它不必读取整个行,毕竟索引叶子节点存储了他们索引的数据;当能通过读取索引就可以得到想要的数据, 那就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。

  • 注意:1. 如果要使用覆盖索引,一定要注意 select 列表汇总只取出需要的列,不可 select * ;2. 因为如果将所有字段一起做索引将会导致索引文件过大,查询性能下降。

4. Using Where

表明使用了 where 过滤

5. using join buffer

使用了链接缓存

6. impossible where

explain select * from t1 where 1=2;
复制代码

where 子句的值总是 false , 不能用来获取任何元组

7. select tbale optimized away

在没有 GROUPBY 子句的情况下,基于索引优化 MIN/MAX 操作或者对于 MyISAM 存储引擎优化 COUT(*) 操作不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。

8. distinct

优化 distinct 操作 在找到第一匹配的元祖后立即停止找相同值的动作。

举个例子

例子描述:

explain select d1.name, (select id from t3) d2 from 
(select id, name from t1 where other_column = '') d1
union
(select name, id from t2);
复制代码

查询结果:案例解析:

  • 第一行 (执行顺序4):id 列为1 , 表示 union 的第一个 select , select_type 的 primary 表表示该查询为外层查询, table

  • 列被标记为 , 表示查询结果来自一个衍生表,其中 derived3 中的 3 代表查询衍生自第三个 select 查询, 即 id 为 3 的 select [select d1.name ... ]

  • 第二行(执行顺序为2):id 为 3 ,是整个查询中第三个 select 的一部分, 因查询包含在from 中, 所以为derived 。【select id, name from where other_column = ''】

  • 第三行(执行顺序为3):select 列表中的子查询 select_type 为 subquery , 为整个查询中的第二个 select . [select id from t3]

  • 第四行(执行顺序为1):select_type 为 union , 说明第四个 select 是 unin 里的第二个 select , 最先执行 【select name ,id from t2】

  • 第五行(执行顺序为5):代表 union 的临时表中读取行的阶段, table 列的 表示用第一个 和第四个 select 结果进行union 操作 。【两个结果 union 操作】

参考资料

  • mysql.com

  • dev.mysql.com/doc/refman/…


作者:老郑_
链接:https://juejin.cn/post/7025079353608257550
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。



浏览 36
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报