mysql之explain

⊙ 使用EXPLAIN语法检查查询执行计划
   ◎ 查看索引的使用情况
   ◎ 查看行扫描情况
⊙ 避免使用SELECT *
   ◎ 这会导致表的全扫描
   ◎ 网络带宽会被浪费

话说工欲善其事,必先利其器。今天就简单介绍下EXPLAIN。

内容导航

  • id
  • select_type
  • table
  • type
  • possible_keys
  • key
  • key_len
  • ref
  • rows
  • Extra

 

环境准备

MySQL版本:

创建测试表

复制代码
CREATE TABLE people(id bigint auto_increment primary key,zipcode char(32) not null default '',address varchar(128) not null default '',lastname char(64) not null default '',firstname char(64) not null default '',birthdate char(10) not null default ''
);CREATE TABLE people_car(people_id bigint,plate_number varchar(16) not null default '',engine_number varchar(16) not null default '',lasttime timestamp
);
复制代码

插入测试数据

复制代码
insert into people
(zipcode,address,lastname,firstname,birthdate)
values
('230031','anhui','zhan','jindong','1989-09-15'),
('100000','beijing','zhang','san','1987-03-11'),
('200000','shanghai','wang','wu','1988-08-25')insert into people_car
(people_id,plate_number,engine_number,lasttime)
values
(1,'A121311','12121313','2013-11-23 :21:12:21'),
(2,'B121311','1S121313','2011-11-23 :21:12:21'),
(3,'C121311','1211SAS1','2012-11-23 :21:12:21')
复制代码

创建索引用来测试

alter table people add key(zipcode,firstname,lastname);

 

EXPLAIN 介绍

 先从一个最简单的查询开始:

Query-1
explain select zipcode,firstname,lastname from people;

EXPLAIN输出结果共有id,select_type,table,type,possible_keys,key,key_len,ref,rows和Extra几列。

id

Query-2
explain select zipcode from (select * from people a) b;

id是用来顺序标识整个查询中SELELCT 语句的,通过上面这个简单的嵌套查询可以看到id越大的语句越先执行。该值可能为NULL,如果这一行用来说明的是其他行的联合结果,比如UNION语句:

Query-3
explain select * from people where zipcode = 100000 union select * from people where zipcode = 200000;

 

select_type

SELECT语句的类型,可以有下面几种。

SIMPLE

最简单的SELECT查询,没有使用UNION或子查询。见Query-1。

 

PRIMARY

在嵌套的查询中是最外层的SELECT语句,在UNION查询中是最前面的SELECT语句。见Query-2和Query-3。

 

UNION

UNION中第二个以及后面的SELECT语句。 见Query-3。

 

DERIVED

派生表SELECT语句中FROM子句中的SELECT语句。见Query-2。

 

UNION RESULT

一个UNION查询的结果。见Query-3。

 

DEPENDENT UNION

顾名思义,首先需要满足UNION的条件,及UNION中第二个以及后面的SELECT语句,同时该语句依赖外部的查询。

Query-4
explain select * from people where id in  (select id from people where zipcode = 100000 union select id from people where zipcode = 200000 );

Query-4中select id from people where zipcode = 200000的select_type为DEPENDENT UNION。你也许很奇怪这条语句并没有依赖外部的查询啊。

这里顺带说下MySQL优化器对IN操作符的优化,优化器会将IN中的uncorrelated subquery优化成一个correlated subquery(关于correlated subquery参见这里)。

SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);

类似这样的语句会被重写成这样:

SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);

所以Query-4实际上被重写成这样:

Query-5
explain select * from people o where exists  (select id from people where zipcode = 100000 and id = o.id union select id from people where zipcode = 200000  and id = o.id);

题外话:有时候MySQL优化器这种太过“聪明” 的做法会导致WHERE条件包含IN()的子查询语句性能有很大损失。可以参看《高性能MySQL第三版》6.5.1关联子查询一节

 

SUBQUERY

子查询中第一个SELECT语句。

Query-6
explain select * from people  where id =  (select id from people where zipcode = 100000);

 

DEPENDENT SUBQUERY

和DEPENDENT UNION相对UNION一样。见Query-5。

除了上述几种常见的select_type之外还有一些其他的这里就不一一介绍了,不同MySQL版本也不尽相同。

 

table

显示的这一行信息是关于哪一张表的。有时候并不是真正的表名。

Query-7
explain select * from (select * from (select * from people a) b ) c;

可以看到如果指定了别名就显示的别名。

<derivedN>N就是id值,指该id值对应的那一步操作的结果。

还有<unionM,N>这种类型,出现在UNION语句中,见Query-4。

注意:MySQL对待这些表和普通表一样,但是这些“临时表”是没有任何索引的。

 

type

type列很重要,是用来说明表与表之间是如何进行关联操作的,有没有使用索引。MySQL中“关联”一词比一般意义上的要宽泛,MySQL认为任何一次查询都是一次“关联”,并不仅仅是一个查询需要两张表才叫关联,所以也可以理解MySQL是如何访问表的。主要有下面几种类别。

const

当确定最多只会有一行匹配的时候,MySQL优化器会在查询前读取它而且只读取一次,因此非常快。const只会用在将常量和主键或唯一索引进行比较时,而且是比较所有的索引字段。people表在id上有一个主键索引,在(zipcode,firstname,lastname)有一个二级索引。因此Query-8的type是const而Query-9并不是:

Query-8
explain select * from people where id=1;

Query-9
explain select * from people where zipcode = 100000;

注意下面的Query-10也不能使用const table,虽然也是主键,也只会返回一条结果。

Query-10
explain select * from people where id >2;

 

system

这是const连接类型的一种特例,表仅有一行满足条件。

Query-11
explain select * from (select * from people where id = 1 )b;

<derived2>已经是一个const table并且只有一条记录。

 

eq_ref

eq_ref类型是除了const外最好的连接类型,它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY KEY。

需要注意InnoDB和MyISAM引擎在这一点上有点差别。InnoDB当数据量比较小的情况type会是All。我们上面创建的people 和 people_car默认都是InnoDB表。

Query-12
explain select * from people a,people_car b where a.id = b.people_id;

我们创建两个MyISAM表people2和people_car2试试:

复制代码
CREATE TABLE people2(id bigint auto_increment primary key,zipcode char(32) not null default '',address varchar(128) not null default '',lastname char(64) not null default '',firstname char(64) not null default '',birthdate char(10) not null default ''
)ENGINE = MyISAM;CREATE TABLE people_car2(people_id bigint,plate_number varchar(16) not null default '',engine_number varchar(16) not null default '',lasttime timestamp
)ENGINE = MyISAM;
复制代码
Query-13
explain select * from people2 a,people_car2 b where a.id = b.people_id;

我想这是InnoDB对性能权衡的一个结果。

eq_ref可以用于使用 = 操作符比较的带索引的列。比较值可以为常量或一个使用在该表前面所读取的表的列的表达式。如果关联所用的索引刚好又是主键,那么就会变成更优的const了:

Query-14
explain select * from people2 a,people_car2 b where a.id = b.people_id and b.people_id = 1;

 

ref

这个类型跟eq_ref不同的是,它用在关联操作只使用了索引的最左前缀,或者索引不是UNIQUE和PRIMARY KEY。ref可以用于使用=或<=>操作符的带索引的列。

为了说明我们重新建立上面的people2和people_car2表,仍然使用MyISAM但是不给id指定primary key。然后我们分别给id和people_id建立非唯一索引。

reate index people_id on people2(id);
create index people_id on people_car2(people_id);

然后再执行下面的查询:

Query-15
explain select * from people2 a,people_car2 b where a.id = b.people_id and a.id > 2;

Query-16
explain select * from people2 a,people_car2 b where a.id = b.people_id and a.id = 2;

Query-17
explain select * from people2 a,people_car2 b where a.id = b.people_id;

Query-18
explain select * from people2 where id = 1;

看上面的Query-15,Query-16和Query-17,Query-18我们发现MyISAM在ref类型上的处理也是有不同策略的。

对于ref类型,在InnoDB上面执行上面三条语句结果完全一致。

 

fulltext

链接是使用全文索引进行的。一般我们用到的索引都是B树,这里就不举例说明了。

 

ref_or_null

该类型和ref类似。但是MySQL会做一个额外的搜索包含NULL列的操作。在解决子查询中经常使用该联接类型的优化。(详见这里)。

Query-19
mysql> explain select * from people2 where id = 2 or id is null;

Query-20
explain select * from people2 where id = 2 or id is not null;

注意Query-20使用的并不是ref_or_null,而且InnnoDB这次表现又不相同(数据量大的情况下有待验证)。

 

index_merger

该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。关于索引合并优化看这里。

 

unique_subquery

该类型替换了下面形式的IN子查询的ref:

value IN (SELECT primary_key FROM single_table WHERE some_expr)

unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。

 

index_subquery

该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引:

value IN (SELECT key_column FROM single_table WHERE some_expr)

 

range

只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。key_len包含所使用索引的最长关键元素。在该类型中ref列为NULL。当使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比较关键字列时,可以使用range:

Query-21
explain select * from people where id = 1 or id = 2;


注意在我的测试中:发现只有id是主键或唯一索引时type才会为range。

这里顺便挑剔下MySQL使用相同的range来表示范围查询和列表查询。

explain select * from people where id >1;

 explain select * from people where id in (1,2);

但事实上这两种情况下MySQL如何使用索引是有很大差别的:

我们不是挑剔:这两种访问效率是不同的。对于范围条件查询,MySQL无法使用范围列后面的其他索引列了,但是对于“多个等值条件查询”则没有这个限制了。

——出自《高性能MySQL第三版》

 

index

该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。这个类型通常的作用是告诉我们查询是否使用索引进行排序操作。

Query-22
explain select * from people order by id;

至于什么情况下MySQL会利用索引进行排序,等有时间再仔细研究。最典型的就是order by后面跟的是主键。

 

ALL

最慢的一种方式,即全表扫描。

 

总的来说:上面几种连接类型的性能是依次递减的(system>const),不同的MySQL版本、不同的存储引擎甚至不同的数据量表现都可能不一样。

 

possible_keys

possible_keys列指出MySQL能使用哪个索引在该表中找到行。

 

key

key列显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

 

key_len

key_len列显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。使用的索引的长度。在不损失精确性的情况下,长度越短越好 。

 

ref

ref列显示使用哪个列或常数与key一起从表中选择行。

 

rows

rows列显示MySQL认为它执行查询时必须检查的行数。注意这是一个预估值。

 

Extra

Extra是EXPLAIN输出中另外一个很重要的列,该列显示MySQL在查询过程中的一些详细信息,包含的信息很多,只选择几个重点的介绍下。

Using filesort 

MySQL有两种方式可以生成有序的结果,通过排序操作或者使用索引,当Extra中出现了Using filesort 说明MySQL使用了后者,但注意虽然叫filesort但并不是说明就是用了文件来进行排序,只要可能排序都是在内存里完成的。大部分情况下利用索引排序更快,所以一般这时也要考虑优化查询了。

 

Using temporary

说明使用了临时表,一般看到它说明查询需要优化了,就算避免不了临时表的使用也要尽量避免硬盘临时表的使用。

 

Not exists

MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行, 就不再搜索了。

 

Using index 

说明查询是覆盖了索引的,这是好事情。MySQL直接从索引中过滤不需要的记录并返回命中的结果。这是MySQL服务层完成的,但无需再回表查询记录。

 

Using index condition

这是MySQL 5.6出来的新特性,叫做“索引条件推送”。简单说一点就是MySQL原来在索引上是不能执行如like这样的操作的,但是现在可以了,这样减少了不必要的IO操作,但是只能用在二级索引上,详情点这里。

 

Using where

使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。

注意:Extra列出现Using where表示MySQL服务器将存储引擎返回服务层以后再应用WHERE条件过滤。

 

EXPLAIN的输出内容基本介绍完了,它还有一个扩展的命令叫做EXPLAIN EXTENDED,主要是结合SHOW WARNINGS命令可以看到一些更多的信息。一个比较有用的是可以看到MySQL优化器重构后的SQL。

 

引用自:http://www.cnblogs.com/magialmoon/p/3439042.html

           http://www.cnblogs.com/magialmoon/p/3472804.html#anzhuang

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544247.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud OpenFeign + Nacos正确打开方式!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;Nacos 支持两种 HTTP 服务请求&#xff0c;一个是 REST Template&#xff0c;另一个是 Feign Client。之前的文章咱们介绍过…

为什么创建线程池一定要用ThreadPoolExecutor?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Java 语言中&#xff0c;并发编程都是依靠线程池完成的&#xff0c;而线程池的创建方式又有很多&#xff0c;但从…

Objects.equals有坑

前言最近review别人代码的时候&#xff0c;发现有个同事&#xff0c;在某个业务场景下&#xff0c;使用Objects.equals方法判断两个值相等时&#xff0c;返回了跟预期不一致的结果&#xff0c;引起了我的兴趣。原本以为判断结果会返回true的&#xff0c;但实际上返回了false。记…

Java 18 正式发布,默认 UTF-8,finalize 被弃用,别再乱用了!

JDK 18 正式发布JDK 17 刚发布半年&#xff0c;JDK 18 又如期而至&#xff0c;JDK 版本号这算是成年了&#xff1f;JDK 18 发布了&#xff0c;栈长继续为大家解读&#xff01;JDK 18 延续了 JDK 17 开创的免费策略&#xff0c;但&#xff0c;JDK 18&#xff5e;20 不是长期支持…

Spring官方推荐的@Transactional还能导致生产事故?

在Spring中进行事务管理非常简单&#xff0c;只需要在方法上加上注解Transactional&#xff0c;Spring就可以自动帮我们进行事务的开启、提交、回滚操作。甚至很多人心里已经将Spring事务与Transactional划上了等号&#xff0c;只要有数据库相关操作就直接给方法加上Transactio…

京东二面:MySQL 主从延迟、读写分离 7 种解决方案!

我们都知道互联网数据有个特性&#xff0c;大部分场景都是 读多写少&#xff0c;比如&#xff1a;微博、微信、淘宝电商&#xff0c;按照 二八原则&#xff0c;读流量占比甚至能达到 90%结合这个特性&#xff0c;我们对底层的数据库架构也会做相应调整。采用 读写分离处理过程&…

再见Postman,这款API神器更好用!

代码未动&#xff0c;文档先行其实大家都知道 API 文档先行的重要性&#xff0c;但是在实践过程中往往会遇到很多困难。程序员最讨厌的两件事&#xff1a;1. 写文档&#xff0c;2. 别人不写文档。大多数开发人员不愿意写 API 文档的原因是写文档短期收益远低于付出的成本&#…

如何保证数据库和缓存双写一致性?

前言数据库和缓存&#xff08;比如&#xff1a;redis&#xff09;双写数据一致性问题&#xff0c;是一个跟开发语言无关的公共问题。尤其在高并发的场景下&#xff0c;这个问题变得更加严重。我很负责的告诉大家&#xff0c;该问题无论在面试&#xff0c;还是工作中遇到的概率非…

面试官:AtomicInteger是如何保证线程安全?

blog.csdn.net/nanhuaibeian/article/details/120936139一、为什么引入 AtomicInteger &#xff1f;谈到线程安全&#xff0c;会首先想到了synchronized 和 Lock,但是这种方式又有一个名字&#xff0c;叫做互斥锁&#xff0c;一次只能有一个持有锁的线程进入&#xff0c;再加上…

机器学习 训练验证测试_测试前验证| 机器学习

机器学习 训练验证测试In my previous article, we have discussed about the need to train and test our model and we wrote a code to split the given data into training and test sets. 在上一篇文章中&#xff0c;我们讨论了训练和测试模型的必要性&#xff0c;并编写了…

如何判断线程池已经执行完所有任务了?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;很多场景下&#xff0c;我们需要等待线程池的所有任务都执行完&#xff0c;然后再进行下一步操作。对于线程 Thread …

IRCTC的完整形式是什么?

IRCTC&#xff1a;印度铁路餐饮和旅游公司 (IRCTC: Indian Railways Catering and Tourism Corporation) IRCTC is an abbreviation of Indian Railways Catering and Tourism Corporation. It is a subsidiary of the Indian Railway established by the Ministry of Railways…

分布式锁的 3 种实现方案!

前言 大家好&#xff0c;我是磊哥。今天跟大家探讨一下分布式锁的设计与实现。希望对大家有帮助&#xff0c;如果有不正确的地方&#xff0c;欢迎指出&#xff0c;一起学习&#xff0c;一起进步哈~分布式锁概述数据库分布式锁Redis分布式锁Zookeeper分布式锁三种分布式锁对比1.…

java学习笔记16--异常

java学习笔记16--异常 异常 异常时导致程序中断运行的一种指令流&#xff0c;如果不对异常进行正确的处理&#xff0c;则可能导致程序的中断执行&#xff0c;造成不必要的损失&#xff0c; 所以在程序的设计中必须要考虑各种异常的发生&#xff0c;并正确的做好相应的处理&am…

线程安全问题的 3 种解决方案!

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;线程安全是指某个方法或某段代码&#xff0c;在多线程中能够正确的执行&#xff0c;不会出现数据不一致或数据污染的…

一文读懂MySQL查询语句的执行过程

需要从数据库检索某些符合要求的数据&#xff0c;我们很容易写出 Select A B C FROM T WHERE ID XX 这样的SQL&#xff0c;那么当我们向数据库发送这样一个请求时&#xff0c;数据库到底做了什么&#xff1f;我们今天以MYSQL为例&#xff0c;揭示一下MySQL数据库的查询过程&a…

synchronized底层是如何实现的?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;想了解 synchronized 是如何运行的&#xff1f;就要先搞清楚 synchronized 是如何实现&#xff1f;synchronized 同步…

单例模式 4 种经典实现方法

0.前言 如果你去问一个写过几年代码的程序员用过哪些设计模式&#xff0c;我打赌&#xff0c;90%以上的回答里面会带【单例模式】。甚至有的面试官会直接问&#xff1a;说一下你用过哪些设计模式&#xff0c;单例就不用说了。你看&#xff0c;连面试官都听烦了&#xff0c;火爆…

CSRF简单介绍及利用方法-跨站请求伪造

0x00 简要介绍 CSRF&#xff08;Cross-site request forgery&#xff09;跨站请求伪造&#xff0c;由于目标站无token/referer限制&#xff0c;导致攻击者可以用户的身份完成操作达到各种目的。根据HTTP请求方式&#xff0c;CSRF利用方式可分为两种。 0x01 GET类型的CSRF 这种类…

虾皮二面:什么是零拷贝?如何实现零拷贝?

前言 零拷贝是老生常谈的问题啦&#xff0c;大厂非常喜欢问。比如Kafka为什么快&#xff0c;RocketMQ为什么快等&#xff0c;都涉及到零拷贝知识点。最近技术讨论群几个伙伴分享了阿里、虾皮的面试真题&#xff0c;也都涉及到零拷贝。因此本文将跟大家一起来学习零拷贝原理。1.…