mysql_手把手教你理解explain

执行计划:

执行计划字段,黄色部分为重点:

1、ID(重点)

2、select type(了解):simple、primary、subquery、Derived、union、union result
3、partitions:

4、type(重点)

5、possible keys:查询字段中涉及到的索引
6、key:实际使用的索引
7、key_len的计算:索引的长度,主要是为了帮助判断复合索引字段有无失效,对于单索引无意义
8、ref:忽略
9、rows:发送给客户端的行数,太大需关注
10、filtered

11:extra(重点)

---------------------------------------------建表和添加数据--------------------------------------------

1、创建表test

CREATE TABLE `test` (
  `id` int(3) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  `mobilephone` varchar(20) DEFAULT NULL,
  `grade` varchar(5) DEFAULT NULL,
  `class` varchar(20) DEFAULT NULL,
  KEY `idx_grade` (`grade`),
  KEY `idx_mobilephone` (`mobilephone`),
  KEY `idx_id_name_class` (`id`,`name`,`class`)
);

2、给表中添加数据:若是几条也可以直接用insert into,批量数据可用用存储过程,若如下存储过程代码有报错情况,可参见我另一篇博文:https://blog.csdn.net/weixin_39772200/article/details/104164775

drop procedure if exists proc_test;
delimiter $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_test`()
BEGIN
set @i = 1;
set @array_name="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
set @array_grade="one two three four five six";
set @count=CHAR_LENGTH(@array_grade)-CHAR_LENGTH(REPLACE(@array_grade,' ',''))+1;
set @telphone_array="180 181 182 183 186 189 191 152 138 134";
set @count_pre=CHAR_LENGTH(@telphone_array)-CHAR_LENGTH(REPLACE(@telphone_array,' ',''))+1;
while @i<10000 do
set @name=substring(@array_name,round(1+RAND()*45),7);
set @left_num=FLOOR(1+rand()*@count);
set @grade=SUBSTRING_INDEX(SUBSTRING_INDEX(@array_grade,' ',@left_num),' ',-1);
set @count_left= FLOOR(1+rand()*@count_pre);
set @mphone=SUBSTRING_INDEX(SUBSTRING_INDEX(@telphone_array,' ',@count_left),' ',-1);
set @mobilephone=CONCAT(@mphone,FLOOR(rand()*90000000+10000000));
if @i/10<11 then
set @class="class_one";
elseif @i/10>=11 && @i/10<21 then
  set @class="class_two";
elseif @i/10>=21 && @i/10<31 then
  set @class="class_three";
elseif @i/10>=31 && @i/10<41 then
  set @class="class_four";
elseif @i/10>=41 && @i/10<51 then
  set @class="class_five";
elseif @i/10>=51 && @i/10<61 then
  set @class="class_six";
elseif @i/10>=61 && @i/10<71 then
  set @class="class_seven";
elseif @i/10>=71 && @i/10<81 then
  set @class="class_eight";
elseif @i/10>=81 && @i/10<91 then
  set @class="class_nine";
elseif @i/10>=91 && @i/10<101 then
  set @class="class_ten";
elseif @i/10>=101 && @i/10<121 then
  set @class="class_eleven";
END IF;
    insert into test (id,name,grade,class,mobilephone)value(@i,@name,@grade,@class,@mobilephone);
set @i=@i+1;
  END while;
END
$$
delimiter ;

3、调用存储过程 mysql> call proc_test(),至此9999条数据添加完成

--------------------------------------------第一部分:ID--------------------------

结论:ID大的先执行,ID小的后执行,ID相同时执行顺序从上到下,数据库性能一定和数据量、硬件联系在一起才有意义


-----------------------------------------第二部分:select type-------------

目录:

1、simple:不包括子查询和union

mysql> explain select id,name from test where id>15;

在这里插入图片描述
2、primary:包括子查询,主查询为最外层
3、subquey:包括子查询,子查询为非最外层

mysql> explain select * from test t2 where t2.id > (select t1.id from test t1 where t1.id =20);

在这里插入图片描述
4、Derived:衍生查询,查询过程中使用了临时表
5、union:union后的第一个select标记为union,如下则t2会标记为union,若union包含在from子句的子查询 中则最外层的select标记为derived,如下
select t.id,t.name
from (select * from test t1 where t1.id=1
union
select * from test t2 where t2.id=20) t;
6、union result:查询表用到了union结果集

Derived、union、union result举例:

explain select t.id,t.name from (select * from test where id=1 union select * from test where id=20) t ;

在这里插入图片描述

-----------------------------------------------第三部分:type--------------------

type类型按从优到劣的顺序:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

1、常出现的有:

  • 理想状态:system、const
  • 优化后尽可能达到的:eq_ref 、 ref
  • 不太理想的:range、index、all

2、const:
system:只有一条数据的系统表或衍生表只有一条数据
const:扫描一行就可获得,通常是扫描主键索引或唯一索引才能达到。

实验一:复合索引:idx_id_name_class (id,name,class)

 mysql> explain select * from test where id=1;

在这里插入图片描述
举例二:id上建主键索引,可以达到const

mysql> drop index idx_id_name_class on test;
mysql> alter table test add primary key idx_id(id);
mysql> explain select * from test where id=1;

在这里插入图片描述
举例三:id上建唯一索引,可达到const

mysql> alter table test drop primary key;
mysql> alter table test add unique key idx_id(id);
mysql> explain select * from test where id=1;

在这里插入图片描述

3、ef_req:非驱动表的关联值为主键索引或唯一索引,返回匹配行=1

举例一:关联字段id为唯一索引,非驱动表为eq_ref

mysql> explain select * from test t1 join test t2 on t1.id=t2.id;

在这里插入图片描述

举例二:关联字段id为主键索引,非驱动表为eq_ref

mysql> drop index idx_id;
mysql> alter table test add primary key idx_id(id);
mysql> explain select * from test t1 join test t2 on t1.id=t2.id;

在这里插入图片描述

4、ref:非驱动表关联字段为非唯一索引,返回匹配行>=0

举例:非驱动表关联字段为id,id索引为非唯一索引

mysql> drop index idx_id;
mysql> alter table test add index idx_id(id);
mysql> explain select * from test t1 join test t2 on t1.id=t2.id;

在这里插入图片描述

5、range:索引范围扫描,常见操作为:>,>=,<,<=,between……and

在这里插入图片描述

6、index:索引全表扫描,和ALL有点相似,不同的是ALL是扫描全部的数据,index扫描的是全部索引

mysql> explain select id from test where id>=60;

在这里插入图片描述

7、All:全表扫描,分为两种,一种是未建索引,一种是索引失效

第一种:未建索引

explain select id,name from test where id=10;

在这里插入图片描述
第二种:索引失效。 索引失效又分如下几种情况:
表中的索引:
在这里插入图片描述

  • a. 索引列上有计算
mysql>explain select * from test where id*4>100;

优化后:放在计算公式的右边

mysql>explain select * from test where id>100/4;

在这里插入图片描述

  • b.like百分号前置
 mysql> explain select * from test where mobilephone like '%83%';

优化,尽量使用百分号后置,eg:like ‘83%’;
在这里插入图片描述

  • c.复合索引最佳左前缀原则

eg:idx_id_name_age_address(id,name,age,address)
第一种带头大哥不能死:查询条件未使用id,所有的索引失效:
第二种索引跨列:使用了id,没有使用name,则name之后的所有索引失效
实验一:

  1、创建复合索引:
  mysql> create index idx_id_name_class on table(id,name,class);
  2、大头大哥死了,索引列全部失效,eg:
  mysql> explain select * from test where name='wxj' ;
  或
  mysql> explain select * from test where class='one';
  或
  mysql> explain select * from test where name='wxj' and class='one';
  3、跨列,部分索引失效:
  mysql> explain select id,name,class from test where id='1' and class='one';
  

索引失效图:在这里插入图片描述
在这里插入图片描述
3、索引生效

  mysql> explain select * from test where id =1;
  mysql> explain select * from test where id =1 and name='wxj' and class='one';
  mysql> explain select * from test where id =1 and name='wxj';

根据

  • d.or连接

优化:可尝试用union改写,因sql优化是概率事件,所以不一定有效

mysql> explain select * from test where id =1 or mobilephone like '186%';
mysql>explain select * from test where id =1 union select * from test where  mobilephone like '186%';

在这里插入图片描述

  • e.select *
 explain select * from test where grade is null;
  • f.索引列上有NULL值
 mysql> explain select * from test where grade is null;

在这里插入图片描述

  • g.索引列上有NOT值

NOT值包括:is not null ,not in,<>,not exists

 explain select * from test where id is not null;
 explain select * from test where id <>10;
 explain select * from test where id not in(10,19,20);
 explain select * from test t1 where not exists(select 1999 from test t2 where t2.id>t2.id);

在这里插入图片描述
-----------------------------------第四部分:Extra----------------------

Extra 额外信息:包含在其他列不便展示但很重要的额外信息,类型包含如下:

  • using where :需要回表查询
  • impossible where :where字句永远为假
  • using index :使用了覆盖索引(索引包含了查询和返回列,不需要回表读取)
  • using index condition
  • using filesort:性能消耗大,额外排序,通常是由于order by产生
  • using temporary:性能消耗大,一般是用到了临时表,主要是group by字段和查询的字段不同

复合索引:idx_id_name_class (id,name,class),
单索引: idx_mobilephone (mobilephone),
单索引: idx_grade (grade)

1、using index :使用了覆盖索引(索引包含了查询和返回列,不需要回表读取)
explain select id,name from test order by id;
explain select id,name from test order by id,name;
explain select id,name from test order by id,name,class;
explain select id,name from test order by name;
2、using filesort:性能消耗大,额外排序,通常是由于order by产生
  • 2.1 where语句和order by语句使用的索引不同
eg:explain select * from test where id>10 order by grade;
  • 2.2 where语句和order by语句组合满足最左前缀,但where字句使用了条件查询
- 复合索引idx_id_name_class(id,name,class)
eg:
explain select * from test where id>10 order by name;
explain select * from test where id<10 order by name;

在这里插入图片描述

  • 2.3 order by中使用了不同的索引列
explain select id,grade from test  order by id,grade;

在这里插入图片描述

  • 2.4 没有where子句,且查询的列和order by的列不一致
explain select grade from test  order by id,name;

在这里插入图片描述

  • 2.5 对索引列同时进行了desc和asc
explain select * from test where id<10 order by id asc,name desc;

在这里插入图片描述

  • 2.6 order by中的索引使用表达式
 explain select * from test where id<10 order by id=3,name;
3、using temporary:性能消耗大,一般是用到了临时表,主要是group by字段和查询的字段不同
CREATE TABLE `ta` (
  `ta_id` int(11) NOT NULL AUTO_INCREMENT,
  `ta_name` varchar(30) DEFAULT NULL,
  `address` varchar(30) DEFAULT '江西省上饶市',
  PRIMARY KEY (`ta_id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8
  • 3.1 主键使用了distinct字段
explain select distinct ta_id,t2.* from ta ,test t2 where ta_id=t2.id;
优化去除distinct:explain select distinct ta_id,t2.* from ta ,test t2 where ta_id=t2.id;

在这里插入图片描述

  • 3.2 group by子句和查询列不一致
explain select grade from test group by id,grade;

在这里插入图片描述

4、using index condition:索引范围扫描
explain select * from test where id=1 order by name;
explain select * from test where id<10;

在这里插入图片描述

5、impossible where :where子句中的条件永远为假
mysql> explain select * from test where id=1 and id=2;

猜你喜欢

转载自blog.csdn.net/weixin_39772200/article/details/104149321
今日推荐