MySQL落实计划explain的全面解析

原创
小哥 3年前 (2022-10-29) 阅读数 161 #大杂烩

1.如何查看SQL执行计划:

在MySQL在中生成执行计划的方法很简单。SQL该语句位于语句之前 explain 既可。
explain可以对其采取行动SELECT/INSERT/UPDATE/DELETE和REPLACE语句。
本文中的示例中涉及的表和数据在数据准备脚本的第三部分中进行了测试。 。

2.执行计划输出栏解释:

当使用explain如果设置为,则输出中包含的列为:
id,select_type,table,type,possible_keys,key,key_len,ref,rows,filtered, Extra

0、总览

以下是一些例子:

explain select student.sname, sc.score from student, sc, teacher 
where student.s_id = sc.s_id and sc.t_id = teacher.t_id 
and teacher.tname = tname16;

1、ID

包含一组数字,表示select查询的序列号,用于标识执行顺序。
id相同,自上而下执行顺序;
id不同,id 该值越高,优先级越高,最先执行。
id为NULL的类型为union result, <union 1,2>表示id为1的操作和id为2结果收集的操作和。
重写子查询join查询的情况,ID价价值不会增加。

--子查询示例
explain select * from teacher where t_id = 
(select t_id from teacher where tname = name16);

--union查询举例
explain 
select sname from student where sno = 100
union all
select sname from student where sno = 200
union all
select sname from student where sno = 300;

--id为NULL的类型为union result
explain 
select sname from student where sno = 100
union
select sname from student where sno = 200

--重写子查询join查询的情况,ID价值不会增加
explain
select c_id from sc where t_id in (select t_id from teacher where tname = name18);

2、SELECT_TYPE

SELECT查询的类型主要是复杂查询,如普通查询、联合查询和子查询。
SIMPLE:
简单的select查询,则该查询不包含子查询或union查询。
PRIMARY:
该查询包含任何复杂的子部分(子查询或union查询),则标记最外层的查询。primary。
SUBQUERY:
在select 或 where该列表包含子查询,即第一个子查询内部查询。SELECT,结果不依赖于外部查询结果集。
DEPENDENT SUBQUERY:
在子查询中第一个内层。SELECT,取决于外部查询的结果集(子查询的结果集)。*外部查询结果);
UNCACHEABLE SUBQUERY:
对于无法缓存在结果集中的子查询,必须为外部查询中的每一行重新计算(动态计算、耗时的操作)。
DERIVED:
在from列表中包括的子查询被标记derived(派生查询),mysql或者递归地执行这些子查询并将结果放入临时表中。
UNION:
UNION声明中的第二个SELECT在一切开始之后SELECT,第一个SELECT为PRIMARY;若union包含在from在子句的子查询中,外层。select将被标记为derived。
DEPENDENT UNION:
在子查询中UNION,且为UNION 从第二个开始SELECT 在一切开始之后SELECT,还取决于外部查询的结果集;
UNION RESULT:
UNION合并导致,id列为null,table该列显示进行了哪些查询的结果。union。
MATERIALIZED:
物化子查询可以用作临时表来存储子查询的结果。

关于dependent subquery和uncacheable subquery,官方文件对此做出了解释:
DEPENDENT SUBQUERY evaluation differs from UNCACHEABLE SUBQUERY evaluation. For DEPENDENT SUBQUERY, the subquery is re-evaluated only once for each set of different values of the variables from its outer context. For UNCACHEABLE SUBQUERY, the subquery is re-evaluated for each row of the outer context.
翻译如下:
dependent subquery评估和uncacheable subquery不同的评估。dependent subquery外部查询中的不同值只计算一次。
而uncacheable subquery重新计算外部查询中的每一行。(可见两者的执行效率完全不同,极端情况下可以相差几个数量级)

3、TABLE

查询结果来自哪个表。它可以是特定的表名或表的别名,也可以是下列值:
union M,N:id为M和N查询结果是这样的。union
derivedN:参考id为N查询的派生查询。
subqueryN:参考id为N物化子查询

4、TYPE

表连接类型、取数方式。
type显示访问类型,是一个比较重要的指标,结果值从好到差是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
通常,有必要确保查询至少到达range级别,最好是实现ref。


system :表中只有一行数据(=system table),是const排字的特殊情况。
const :表中只有一行符合条件,将在查询开始时读取该行。由于只有一行,优化器可以将此值视为常量。const连接类型非常快,因为它只读一次。它通常用于对主键或唯一索引执行等价查询。


eq_ref:可以理解为使用主键或唯一非空索引来建立等价的唯一连接。从表中取出一行,并将其与上一张表中取出的行连接起来。不同于system和const这是最常见的连接方法。
这是指将索引中使用的所有内容用作连接,并且索引是主键或唯一的非空索引。eq_ref将索引列用作等效连接。与之比较的值可以是常量,也可以是与上一个表的列相关的表达式。
ref:可以理解为非唯一索引扫描。从索引中取出相应值的行,并将其与从上一个表中取出的数据连接起来。如果仅使用索引的左前缀,或者如果非主键不是用于链接的唯一索引,则使用ref。(换句话说,对于给定值,将返回多行。)如果给定值只返回几行,这是一种很好的连接模式。ref可用于索引列。=或>=,<=操作。

fulltext:全文索引。
ref_or_null:连接模式和ref相似,但添加了包含以下内容的附加搜索条件null价值。这种连接优化的大部分用于解决子查询问题。


index merge:此连接表示使用index merge优化,查询使用表中的多个索引。在这种情况下,explain在输出的列中,key该列包含使用的索引的列表,key_len该列包含所用列的最大长度列表。

unique_subquery:这种方法在使用中是用的。in子查询关键字时,将替换该关键字。ref连接模式。
unique_subquery是一种用来代替子查询的索引查询方法,以提供更高的效率。
index_subquery:此连接类似unique_subquery,代替了IN方法,但用于不是唯一索引的子查询。
range:索引范围查询。使用索引查询返回给定范围内的行。在……里面explain在输出中,key列显示使用了哪个索引,key_len该列显示了所使用的列的最长部分,在这种情况下,ref列为null。range用于key将列与常量进行比较,运算符可以是 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, 或者 IN。


index:index: 连接类型如下 all 不同之处在于,它只扫描索引树。
有两种情况:
(1如果该索引是覆盖索引,则可以从该索引获取所有必需的数据,并且此时仅扫描该索引。在这种情况下,explain命令在输出中,Extra列显示 Using index。它通常是更好的 all快点,因为索引文件通常比数据文件小。
(2通过查询索引,按索引的顺序执行全表扫描。在这种情况下,Extra不显示列using index。


all:全表扫描。

5、possible_keys

指 mysql在搜索表记录时可以使用该索引。
possible_keys其中包含的索引可能不会在实际使用中使用。如果此字段的值为null,这意味着不使用任何索引。
在这种情况下,您可以检查 where子句中的哪些字段适合添加索引以提高查询性能。

6、key

key现场展示mysql要实际使用的索引。
未使用索引时,此字段的值为null。
想要让mysql强迫使用或忽视 possible_keys字段中的索引列表可以在查询语句中使用。force index, use index,或 ignore index。
如果是 myisam 和 bdb 类型表,您可以使用 analyzetable 以帮助分析使用哪个索引更好。
如果是 myisamTYPE表格,运行命令 myisamchk –analyze同样的效果。

7、key_len

显示MySQL确定所用密钥的长度。表示索引中使用的字节数,该字节数可用于计算查询中使用的索引长度。如果密钥是NULL,长度为NULL。该文档提示要特别注意可以从多个主键派生的此值。mysql实际使用的是哪部分。可以在联合指数中告诉你。mysql哪些索引将被实际使用。
注:key_len显示的值是索引字段的最大可能长度,而不是实际使用的长度,即E。key_len根据表定义计算,不通过表检索。
计算方法:
字符串类型,考虑字符串的长度以及是否为空;
数字类型、时间日期类型。

8、ref

ref 现场展示哪些字段或者常量被用来和 key表中的查询记录。

9、rows

rows 现场展示mysql被视为在查询中检索的记录数。 表示MySQL根据表统计和索引选择,估计要读取以查找所需记录的行数。innodb可能是不准确的。

10、extra列包含了mysql处理sql一些额外的信息。

如果您想让查询尽可能快,那么请注意。Extra列中值为using filesort和using temporary。
Using filesort
mysql你需要再做一次,才能让记录有一个良好的顺序。排序器根据连接类型遍历所有记录并匹配所有记录。 where存储条件记录的排序关键字和指向该记录的指针。这些关键字已经被排序,相应的记录将按排序的顺序取出。


Using index
该字段的信息直接从索引树中的信息中获取,而不是扫描实际记录。查询时用于此策略的字段是单独索引的一部分。
如果extra也有using where这意味着该索引用于查找键值,然后返回到表中。
如果没有using where,优化器可能是为了避免返回表。
例如,如果索引时索引被覆盖,优化器将扫描索引,而不是返回索引所在的表。
对于具有聚集索引的表,即使extra列中没有using index,您还可以使用索引。这适用于type列是index,key列是primary的情况。
Using index condition
ICP优化关联,数据过滤条件来自server存储引擎层被下推到存储引擎层,并根据存储引擎层中的原始索引对数据进行过滤,避免了不合格数据的传输。server。(非翻译,从网络组织。)


Using index for group-by
与using index类似,using index for group-by表明mysql查找返回全部的索引。group-by或distinct查询所需的列不需要返回到表中。
Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access)
在建立连接时,当前表之前的表会被多次读入。join buffer中,然后join buffer中的表格后面是当前表格。join。(Block Nested Loop) 表明使用BNL算法。 (Batched Key Access)表明使用BKA算法。explain在中,上一行keys会被读入buffer,然后分批获取当前表的对应行。(你什么意思,再查一遍!)
Using MRR
MRR优化方法,具体再检查一遍。
Using sort_union(…), Using union(…), Using intersect(…)
Using temporary
mysql需要创建临时表来存储结果以完成查询。当查询包含时通常会发生这种情况。group by 和 order by 子句,该子句以不同的方式列出字段。
Using where
筛选符合条件的行。
当extra列中没有using where表连接类型为all或者index除非您想要获取或检查所有行,否则可能执行了错误的查询。
Using where with pushed condition
仅适用于NDB。过滤来自每个节点的数据。

no matching row in const table: 对一个有join查询包含空表或没有数据满足唯一索引条件。

最后添加:

partitions该列显示查询中涉及的分区;
filtered,它是指返回需要读取的结果的行。(rows列的值)百分比。据推测, filtered是一个非常有用的价值,因为join操作时,前一个表的结果集大小直接影响周期数。但我在我的环境中的测试结果是, filtered的价值一直是100%也就是说,失去了意义。

3.数据准备:

--以下是student、course、sc、teacher用于四个表的建表语句和用于插入测试数据的函数语句。

--学生表student建表语句和插入测试数据的函数。
drop table if exists student;
create table student
(
s_id bigint unsigned not null auto_increment,
sno int,
sname varchar(50),
sage int,
ssex varchar(8),
father_id int,
mather_id int,
note varchar(500),
primary key uk_sid(s_id)
);
truncate table student;

delimiter $$
drop function if exists insert_student_data $$
create function insert_student_data()
returns int deterministic
    begin
    declare i int;
    set i = 1;
    while i <= 5000000 do
    insert into student values(i, i, concat(name, i), i, case when floor(rand()*10%2) = 0 then f else m end, 
    floor(rand()*100000),floor(rand()*10000),concat(note,i));
    set i = i + 1;
    end while;
    return 1;
    end $$
delimiter ;
select insert_student_data(); --执行插入测试数据的功能,500一万条数据大约是300秒。
select count(*) from student;

--课程表course建表语句和插入测试数据的函数。
drop table if exists course;
create table course
(
c_id bigint unsigned not null auto_increment,
cname varchar(50),
note varchar(500),
primary key (c_id)
);
truncate table course;

delimiter $$
drop function if exists insert_course_data $$
create function insert_course_data()
returns int deterministic
begin
declare i int;
set i = 1;
while i <= 1000 do
insert into course values (i, concat(course, i), floor(rand() * 1000));
set i = i + 1;
end while;
return 1;
end$$
delimiter ;
select insert_course_data();
select count(*) from course;

--分数表sc建表语句和插入测试数据的函数。
drop table if exists sc;
create table sc
(
s_id int,
c_id int,
t_id int,
score int
);
truncate table sc;

delimiter $$
drop function if exists insert_sc_data $$
create function insert_sc_data()
returns int deterministic
begin
declare i int;
set i = 1;
while i <= 5000000 do
insert into sc values (i, floor(rand()*1000), floor(rand()* 100000), floor(rand()*800));
set i = i + 1;
end while;
return 1;
end $$
delimiter ;
select insert_sc_data();
create index ind_s_id on sc(s_id);
create index ind_t_id on sc(t_id);
create index ind_c_id on sc(c_id);
select count(*) from sc;

--教师表teacher建表语句和插入测试数据的函数。
drop table if exists teacher;
create table teacher
(
t_id bigint unsigned not null auto_increment,
tname varchar(50),
note varchar(500),
primary key (t_id)
);
truncate table teacher;

delimiter $$
drop function if exists insert_teacher_data $$
create function insert_teacher_data()
returns int deterministic
begin
declare i int;
set i = 1;
while i <= 1000000 do
insert into teacher values (i, concat(name,i), concat(note, i));
set i = i + 1;
end while;
return 1;
end $$
delimiter ;
select insert_teacher_data();
commit;
create index ind_tname on teacher(tname);
select count(*) from teacher;

文章来源博客: mysql explain 输出详解_lijingkuan的博客-CSDN博客

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除