【数据库】史上最详细的mysql执行计划总结


前言

了解一个sql在查询时更加详细的信息,就必须要了解mysql执行计划,在此奉上较为全面的mysql执行计划总结,供大家参考。


一、 explain解析

创建表结构

CREATE TABLE `t_news` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL COMMENT '标题',
  `content` text COMMENT '内容',
  `lng` varchar(255) DEFAULT NULL COMMENT '经纬度',
  `lat` varchar(255) DEFAULT NULL,
  `city_name` varchar(255) DEFAULT NULL COMMENT '城市名称',
  `city_id` bigint(20) DEFAULT NULL COMMENT '城市id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=64936 DEFAULT CHARSET=utf8;

此,以新闻表进行测试,如上是新闻表的基本结构,没有添加索引信息。
查看执行计划

explain select * from t_news

在这里插入图片描述
显示如上信息,目前信息不太完整,稍后会一一解释。

1.1 id列

id列表示select的执行顺序,越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行。

1.1.1 案例1

explain select * from t_news where id in (select id from t_news where city_name=‘北京’)

在这里插入图片描述
一般是在sql中有n个select,则会有大于n条记录。出现两行,sql中两个select,因此肯定是一个select对应一条记录。第一行的select_typePRIMARYPRIMARY表示外层sql,由此可见第一行是主键查询。正如通过id查询的sql。在结合sql的逻辑,必须要先查询city_name北京id,因此可以确定id2的记录,先执行。

1.1.2 案例2

explain
select * from t_news where id=1
union all
select * from t_news where id=3

在这里插入图片描述
union结果是放在一个匿名临时表中,临时表不在SQL中出现,因此它的id是NULL。,而且注意最后一条记录的表名称为<union1,2>,这个名字的意思是联合id为1和id为2的并集

1.2 select_type列

select_type表示对应行是一个什么样的查询类型。

SIMPLE

simple简单查询类型。

explain
select * from t_news where id=1

在这里插入图片描述

PRIMARY 和 DEPENDENT SUBQUERY

PRIMARY:表示最外层的select。
DEPENDENT SUBQUERY:表示第一个子查询。另外如果看到这个标识,说明这个是一个较慢的查询语句。确实此表中6w多数据,city_name这一个列也没有索引肯定会比较慢。

explain select * from t_news where id in (select id from t_news where city_name=‘北京’)
在这里插入图片描述

SUBQUERY

DERIVED

SUBQUERY: 不包含在from中的子查询。
DERIVERD :包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表。

explain
select (select 1 from t_news limit 1 ) from (select * from t_news) a where a.id=3

在这里插入图片描述

另外第一行数据中的table名字为<deriverd3>说明是衍生出来的表,名字是由第二行的idselect_type字段得到的。

UNION

UNION RESULT

union: 在union或者 union all 后面的select
union result: 从union临时表检索结果的select

explain select 1 union all select 2

在这里插入图片描述

1.3、table列

这一列表示 explain 的一行正在访问哪个表。当 from子句中有子查询时,table列是 <derivenN> 格式,表示当前查询依赖id=N的查询,于是先执行 id=N 的查询。当有union 时,UNION RESULTtable列的值为<union1,2>,1和2表示参与 unionselectid

explain
select t.* from (
select * from t_news where id= 1
union all
select * from t_news where id=2) t

在这里插入图片描述

1.4 type列

这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行,查找数据行记录的大概范围。
依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL
一般来说,得保证查询达到range级别,最好达到ref。

NULL

NULL: mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。例如:在索引列中选取最小值,可以单独查找索引来完成,不需要在执行时访问表。

explain select max(id) from t_news

在这里插入图片描述

const

system

const:该表最多具有一个匹配行,该行在查询开始时读取。因为只有一行,所以优化器的其余部分可以将这一行中列的值视为常量。 const表非常快,因为它们只能读取一次。
system: 算是const的一个特例。意思是mysql通过执行引擎即可明显知道表中只有一条记录的时候为system。

explain
select * from (select * from t_news where id=1) t

在这里插入图片描述
另外可以通过show WARNINGS;来查看mysql优化之后执行的sql。

explain extended
select id from (select * from t_news where id=1) t;
show WARNINGS;

在这里插入图片描述
如上图所示,mysql引擎会自动优化,最后会执行select '1' AS id from dual这条语句。与子查询结果一致。

eq_ref

eq_ref:被主键索引唯一索引的部分被连接使用 ,最多只会返回一条符合条件的记录。这可能是在 const 之外最好的联接类型了,简单的 select 查询不会出现这种类型。

explain select * from t_news n
left join t_news s on n.id = s.id
limit 100

在这里插入图片描述

ref

ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀(联合索引的部分前缀),索引要和某个值相比较,可能会找到多个符合条件的行。
注:不是一定要连接。只要普通索引即可。

explain select * from sh_area where pid=1
此时 sh_area 的pid有普通btree索引。

在这里插入图片描述
eq_ref和ref区别:
eq_ref:是使用唯一索引或者主键索引会出现。
ref:普通索引,可能对应多条记录会出现。

range

range:范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行。

explain select * from t_news n where id<100

在这里插入图片描述

explain select * from t_news n where id in(1,2,3,4,5,6,7,8)

在这里插入图片描述

index

index:扫描全表索引,这通常比ALL快一些。(index是从索引中读取的,而all是从硬盘中读取)

explain select pid from sh_area
sh_area表中pid 为索引字段。

在这里插入图片描述

ALL

ALL:即全表扫描,意味着mysql需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了

explain select * from t_news

在这里插入图片描述

1.5、possible_keys列

        这一列显示查询可能使用哪些索引来查找。
explain 时可能出现 possible_keys 有列,而 key 显示 NULL 的情况,这种情况是因为表中数据不多,mysql认为索引对此查询帮助不大,选择了全表查询。
        如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查 where 子句看是否可以创造一个适当的索引来提高查询性能,然后用 explain 查看效果。

1.6、key列

        这一列显示mysql实际采用哪个索引来优化对该表的访问。
如果没有使用索引,则该列是 NULL。如果想强制mysql使用或忽视possible_keys列中的索引,在查询中使用 force indexignore index

注意:possible_keys和key这两列可能会有不一样,大家参看以下即可。

1.7、key_len列

        这一列显示了mysql在索引里使用的字节数,通过这个值可以算出具体使用了索引中的哪些列。可以用来查看联合索引中,用到了联合索引的哪几个字段。

key_len计算规则如下:

  • char(n):n字节长度
  • varchar(n):2字节存储字符串长度,如果是utf-8,则长度 3n + 2
  • tinyint:1字节
  • smallint:2字节
  • int:4字节
  • bigint:8字节
  • date:3字节
  • timestamp:4字节
  • datetime:8字节
    如果字段允许为 NULL,需要1字节记录是否为 NULL
    索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。

explain select * from sh_area where pid=1

在这里插入图片描述
pid字段为int类型,另外此字段可以为NULL,所以key_len=4+1=5

1.8 ref列

这一列显示了在key列记录的索引中,所引用的值是什么,
例如索引列引用常量:

explain select * from sh_area where pid=1
pid是普通单列索引

在这里插入图片描述
引用字段名:

explain select * from t_news n
left join sh_area sh on n.city_id=sh.pid
pid为普通单列索引。

在这里插入图片描述

1.9 rows列

这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。

1.10 Extra列

这一列展示的是额外信息。常见的重要值如下

Using index

Using index:查询的列被索引覆盖,并且where筛选条件是索引的前导列,是性能高的表现。一般是使用了覆盖索引(索引包含了所有查询的字段)。对于innodb来说,如果是辅助索引性能会有不少提高。

explain select id from sh_area where id=2

在这里插入图片描述

覆盖索引:查询的列全部是索引列。因此在查询的时候只需要通过索引筛选即可,索引一般是在内存中,在不需要磁盘IO的情况下,速度是很快的。
前导列:联合索引从左到右,前面的列。

Using where

Using where:查询的列未被索引覆盖,where筛选条件非索引的前导列,就是没用到索引的意思。

explain select * from sh_area where shortname=‘北京’
在这里插入图片描述

Using where Using index

Using where Using index:查询的列被索引覆盖,但是where筛选条件没有使用索引列,或者没办法使用索引,意味着无法直接通过索引查找来查询到符合条件的数据

NULL

NULL:查询的列未被索引覆盖,并且where筛选条件是索引的前导列,意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来实现,不是纯粹地用到了索引,也不是完全没用到索引

Using index condition

Using index condition:与Using where类似,查询的列不完全被索引覆盖,where条件中是一个前导列的范围;


总结

        其中type列尤为重要,通过此列即可了解大概的查询效率。另外在使用联合索引使用时key_len列也同样重要,可以通过此列了解是否使用了联合索引的全部列。例如经常会有人异议联合索引使用时,where条件的顺序是否影响使用联合索引。这时即可通过type列和key_len来得到判断。
如有错误的地方,请多多指正。

测试数据地址:测试数据
Explain官方说明

猜你喜欢

转载自blog.csdn.net/qq_30285985/article/details/111085867