Explain详解与索引优化实践

1、何为explain执行计

使用explain关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何使用索引来处理你的SQL查询语句以及连接表,可以分析查询语句或是结构的性能瓶颈,帮助我们选择更好的索引和写出更优化的查询语句。(说白了,就是优化SQL的工具

2、如何使用explain?

在你的SQL查询语句前加上 explain 即可,如explain select * from table,MySQL会在查询上设置一个标记,执行查询时,会返回执行计划的信息,而不是执行这条SQL(如果 from 中包含子查询,仍会执行该子查询,将结果放入临时表)。

3、使用explain的例子

需要使用三张表,分别为 actor 演员表,film 电影表,film_actor 电影-演员关联表。

CREATE TABLE `actor` (
  `id` int(11) NOT NULL COMMENT '主键id',
  `name` varchar(45) DEFAULT NULL COMMENT '演员名称',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into `actor` (`id`, `name`, `update_time`) values('1','a','2020-02-11 22:56:00');
insert into `actor` (`id`, `name`, `update_time`) values('2','b','2020-02-11 22:56:00');
insert into `actor` (`id`, `name`, `update_time`) values('3','c','2020-02-11 22:56:00');

CREATE TABLE `film` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(10) DEFAULT NULL COMMENT '电影名称',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

insert into `film` (`id`, `name`) values('3','film0');
insert into `film` (`id`, `name`) values('1','film1');
insert into `film` (`id`, `name`) values('2','film2');

CREATE TABLE `film_actor` (
  `id` int(11) NOT NULL COMMENT '主键id',
  `film_id` int(11) NOT NULL COMMENT '电影id',
  `actor_id` int(11) NOT NULL COMMENT '演员id',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `idx_film_actor_id` (`film_id`,`actor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into `film_actor` (`id`, `film_id`, `actor_id`, `remark`) values('1','1','1',NULL);
insert into `film_actor` (`id`, `film_id`, `actor_id`, `remark`) values('2','1','2',NULL);
insert into `film_actor` (`id`, `film_id`, `actor_id`, `remark`) values('3','2','1',NULL);

执行完以上SQL后,三张表数据对应如下:

下面展示explain中每个列的信息:

(1)id列

id列的编号是select语句的序列号,有几个 select 就有几个id,并且id的序号是按 select 出现的顺序而增长的(id越大,对应的select语句越先执行,如果id相等,则从上往下执行,id为NULL最后执行)。

MySQL将select查询分为简单查询(SIMPLE)和复杂查询(PRIMARY)。

复杂查询分为三类:简单子查询、派生表(from语句中的子查询)、union查询。

1)简单子查询

执行SQL语句:EXPLAIN SELECT (SELECT 1 FROM actor LIMIT 1) FROM film

2)from子句中的子查询

执行SQL语句:EXPLAIN SELECT id FROM (SELECT id FROM film) AS der

分析:这个查询执行时有个临时表别名为der,外部select查询引用了这个临时表。

3)union查询

执行SQL语句:EXPLAIN SELECT 1 UNION ALL SELECT 1

分析:union结果总是放在一个匿名临时表中,临时表不在SQL中出现,因此它的id为NULL。(不推荐使用union,性能不高)

(2)select_type列

这一列表示对应行是简单还是复杂查询,如果是复杂查询,又是上述三种复杂查询中的哪一种。

1)SIMPLE:简单查询。查询不包含子查询和union。

执行SQL语句:EXPLAIN SELECT * FROM film WHERE id=2

2)PRIMARY:复杂查询中最外层的select。

3)SUBQUERY:包含在select中的子查询(不在from子句中)。

4)DERIVED:包含在from子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(DERIVED的英文含义)。

执行SQL语句:EXPLAIN SELECT (SELECT 1 FROM actor WHERE id=1) FROM (SELECT * FROM film WHERE id=1) der

5)UNION:在union中的第二个和随后的select。

6)UNION RESULT:从union临时表检索结果的select。

执行SQL语句:EXPLAIN SELECT 1 UNION ALL SELECT 1

(3)table列

这一列表示explain的一行正在访问哪个表。

当from子句中有子查询时,table列是<DERIVED N>格式,表示当前查询依赖id=N的查询,于是先执行id=N的查询。

当有union时,UNION RESULT的table列的值为<union 1,2>,1和2表示参与union的select行id。

(4)type列

(温馨提示:以下部分理论有可能解释完还是懵逼,没关系,继续往下看,有实践例子)

这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行,查找数据记录的大概范围。

SQL语句查询效率从最优到最差依次为:system > const > eq_ref > ref > range > index > ALL

一般来说,得保证查询达到range级别,最好达到ref

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/e29ab88dd33cafa3b0071b471ee0ab56.html