[转帖]深入理解mysql-第十章 mysql查询优化-Explain 详解(上)

深入,理解,mysql,第十章,查询,优化,explain,详解 · 浏览次数 : 0

小编点评

**执行计划-id属性** 每个SELECT关键字代表的小查询都定义了一个称之为select_type的属性,实际就是表示了小查询在整个大查询中扮演了一个什么角色。select_type的取值主要包含以下几种: * **名称描述SIMPLE查询语句中不包含UNION、UNION ALL或者子查询的查询都是SIMPLE类型** * **PRIMARY对于包含UNION、UNION ALL或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询的select_type值就是PRIMARY** * **UNION对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION** * **UNION RESULTMySQL选择使用临时表来完成UNION查询的去重工作,针对该临时表的查询的select_type就是UNION RESULT** * **SUBQUERY如果不相关子查询的查询语句不能够转为对应的semi-join的形式,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,外层查询的select_type就是PRIMARY,子查询的select_type就是SUBQUERY** * **DEPENDENT SUBQUERY如果相关子查询的查询语句不能够转为对应的semi-join的形式,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,外层查询的select_type就是PRIMARY,子查询的select_type就是DEPENDENT SUBQUERY** * **DERIVED对于采用物化的方式执行的包含派生表的查询,该派生表对应的子查询的select_type就是DERIVED** * **MATERIALIZED当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时,该子查询对应的select_type属性就是MATERIALIZED** **执行计划-select_type属性** 每个SELECT关键字代表的小查询都定义了一个称之为select_type的属性,实际就是表示了小查询在整个大查询中扮演了一个什么角色。select_type的取值主要包含以下几种: * **名称描述SIMPLE查询语句中不包含UNION、UNION ALL或者子查询的查询都是SIMPLE类型** * **PRIMARY对于包含UNION、UNION ALL或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询的select_type值就是PRIMARY** * **UNION对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION** * **UNION RESULTMySQL选择使用临时表来完成UNION查询的去重工作,针对该临时表的查询的select_type就是UNION RESULT** * **SUBQUERY如果不相关子查询的查询语句不能够转为对应的semi-join的形式,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,外层查询的select_type就是PRIMARY,子查询的select_type就是SUBQUERY** * **DEPENDENT SUBQUERY如果相关子查询的查询语句不能够转为对应的semi-join的形式,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,外层查询的select_type就是PRIMARY,子查询的select_type就是DEPENDENT SUBQUERY** * **DERIVED对于采用物化的方式执行的包含派生表的查询,该派生表对应的子查询的select_type就是DERIVED** * **MATERIALIZED当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时,该子查询对应的select_type属性就是MATERIALIZED**

正文

 

目录

一、初识Explain

二、执行计划-table属性

三、执行计划-id属性

四、执行计划-select_type属性 


  一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划,这个执行计划展示了接下来具体执行查询的方式,比如多表连接的顺序是什么,对于每个表采用什么访问方法来具体执行查询等等。Mysql为我们提供了EXPLAIN语句来帮助我们查看某个查询语句的具体执行计划,从而可以有针对性的提升我们查询语句的性能。

一、初识Explain

    如果我们想看看某个查询的执行计划的话,可以在具体的查询语句前边加一个EXPLAIN,就像这样:

  1. mysql> EXPLAIN SELECT 1;
  2. +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
  5. | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
  6. +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
  7. 1 row in set, 1 warning (0.01 sec)

    然后这输出的这个表格就是所谓的执行计划,其实除了以SELECT开头的查询语句,其余的DELETEINSERTREPLACE以及UPDATE语句前边都可以加上EXPLAIN这个词儿,用来查看这些语句的执行计划,不过我们一般只会对查询语句的性能进行优化。我们把EXPLAIN语句输出的各个列的作用先大致罗列一下:

列名描述
id在一个大的查询语句中每个SELECT关键字都对应一个唯一的id
select_typeSELECT关键字对应的那个查询的类型
table表名
partitions匹配的分区信息
type针对单表的访问方法
possible_keys可能用到的索引
key实际上使用的索引
key_len实际使用到的索引长度
ref当使用索引列等值查询时,与索引列进行等值匹配的对象信息
rows预估的需要读取的记录条数
filtered某个表经过搜索条件过滤后剩余记录条数的百分比
Extra一些额外的信息

下面我们将按照这个建表语句,新建s1,s2表,除id列外其余的列都插入随机值:

  1. CREATE TABLE single_table (
  2. id INT NOT NULL AUTO_INCREMENT,
  3. key1 VARCHAR(100),
  4. key2 INT,
  5. key3 VARCHAR(100),
  6. key_part1 VARCHAR(100),
  7. key_part2 VARCHAR(100),
  8. key_part3 VARCHAR(100),
  9. common_field VARCHAR(100),
  10. PRIMARY KEY (id),
  11. KEY idx_key1 (key1),
  12. UNIQUE KEY idx_key2 (key2),
  13. KEY idx_key3 (key3),
  14. KEY idx_key_part(key_part1, key_part2, key_part3)
  15. ) Engine=InnoDB CHARSET=utf8;

建完表以后,我们后面开始对其执行计划的每个具体属性进行讲解,不过不会严格按照上面执行计划的展示顺序。 

二、执行计划-table属性

    不论我们的查询语句有多复杂,里边儿包含了多少个表,到最后也是需要对每个表进行单表访问的,所以设计MySQL的大叔规定EXPLAIN语句输出的每条记录都对应着某个单表的访问方法,该条记录的table列代表着该表的表名。

我们看一下一个连接查询的执行计划:

  1. mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
  2. +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
  5. | 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
  6. | 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | Using join buffer (Block Nested Loop) |
  7. +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
  8. 2 rows in set, 1 warning (0.01 sec)

    可以看到这个连接查询的执行计划中有两条记录,这两条记录的table列分别是s1s2,这两条记录用来分别说明对s1表和s2表的访问方法是什么。

三、执行计划-id属性

    我们知道我们写的查询语句一般都以SELECT关键字开头,这里id代表的就是这个select的id,比较简单的查询语句里只有一个SELECT关键字,比如单表查询和连表查询。对于连接查询来说,一个SELECT关键字后边的FROM子句中可以跟随多个表,所以在连接查询的执行计划中,每个表都会对应一条记录,但是这些记录的id值都是相同的,可以看第二节的示例。

      针对查询中包含子查询和UNION的SQL语句,每个SELECT关键字都会对应一个唯一的id值,所以会有多个id:

  1. mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
  2. +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
  5. | 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9688 | 100.00 | Using where |
  6. | 2 | SUBQUERY | s2 | NULL | index | idx_key1 | idx_key1 | 303 | NULL | 9954 | 100.00 | Using index |
  7. +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
  8. 2 rows in set, 1 warning (0.02 sec)

     但是这里大家需要特别注意,并不是所有包含子查询的都会是多个id这是因为,查询优化器可能对涉及子查询的查询语句进行重写,从而转换为连接查询(上一章的内容)。所以如果我们想知道查询优化器对某个包含子查询的语句是否进行了重写,直接查看执行计划就好了,比如说:

  1. mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2 WHERE common_field = 'a');
  2. +----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
  5. | 1 | SIMPLE | s2 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9954 | 10.00 | Using where; Start temporary |
  6. | 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | xiaohaizi.s2.key3 | 1 | 100.00 | End temporary |
  7. +----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
  8. 2 rows in set, 1 warning (0.00 sec)

    可以看到,虽然我们的查询语句是一个子查询,但是执行计划中s1s2表对应的记录的id值全部是1,这就表明了查询优化器将子查询转换为了连接查询。

     对于包含UNION子句的查询语句来说,每个SELECT关键字对应一个id值也是没错的,不过还是有点儿特别的东西,比方说下边这个查询:

  1. mysql> EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
  2. +----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
  5. | 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
  6. | 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | NULL |
  7. | NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
  8. +----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
  9. 3 rows in set, 1 warning (0.00 sec)

  这里UNION会把多个查询的结果集合并起来生成内部的临时表,并对结果集中的记录进行去重,所以在内部创建了一个名为<union1, 2>的临时表。当然UNION ALL就不会生成这个临时表。

四、执行计划-select_type属性 

   每一个SELECT关键字代表的小查询都定义了一个称之为select_type的属性,实际就是表示了小查询在整个大查询中扮演了一个什么角色。select_type的取值主要包含以下几种:

名称描述
SIMPLE查询语句中不包含UNIONUNION ALL或者子查询的查询都是SIMPLE类型,当然,连接查询也算是SIMPLE类型。
PRIMARY对于包含UNIONUNION ALL或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询的select_type值就是PRIMARY。
UNION对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION。
UNION RESULTMySQL选择使用临时表来完成UNION查询的去重工作,针对该临时表的查询的select_type就是UNION RESULT,例子上边有,就不赘述了。
SUBQUERY如果不相关子查询的查询语句不能够转为对应的semi-join的形式,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,外层查询的select_type就是PRIMARY,子查询的select_type就是SUBQUERY。
DEPENDENT SUBQUERY如果相关子查询的查询语句不能够转为对应的semi-join的形式,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,外层查询的select_type就是PRIMARY,子查询的select_type就是DEPENDENT SUBQUERY
DEPENDENT UNION在包含UNION或者UNION ALL的大查询中,如果各个小查询都依赖于外层查询的话,那除了最左边的那个小查询之外(DEPENDENT SUBQUERY),其余的小查询的select_type的值就是DEPENDENT UNION
DERIVED对于采用物化的方式执行的包含派生表的查询,该派生表对应的子查询的select_type就是DERIVED。
MATERIALIZED当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时,该子查询对应的select_type属性就是MATERIALIZED。

文章知识点与官方知识档案匹配,可进一步学习相关知识
MySQL入门技能树查询优化 SHOW STATUS58188 人正在系统学习中

与[转帖]深入理解mysql-第十章 mysql查询优化-Explain 详解(上)相似的内容:

[转帖]深入理解mysql-第十章 mysql查询优化-Explain 详解(上)

目录 一、初识Explain 二、执行计划-table属性 三、执行计划-id属性 四、执行计划-select_type属性 一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划,这个执行计划展示了接下来具体执行查询的方式,比如多表连接的顺序是什么,对于每个表采

[转帖]深入理解mysql-第十一章 mysql查询优化-Explain 详解(中)

一、执行计划-type属性 执行计划的一条记录就代表着MySQL对某个表的执行查询时的访问方法,其中的type列就表明了这个访问这个单表的方法具体是什么,比方说下边这个查询: mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';+ + + + + + +

[转帖]深入理解mysql-第十二章 mysql查询优化-Explain 详解(下)

我们前面两章详解了Explain的各个属性,我们看到的都是mysql已经生成的执行计划,那这个执行计划的是如何生成的?我们能看到一些过程指标数据吗?实际mysql贴心为我们提供了执行计划的各项成本评估指标的以及优化器生成执行计划的整个过程的方法。 一、查看执行计划计算的成本数据 我们上边介绍的EXP

[转帖]深入理解mysql-第六章 mysql存储引擎InnoDB的索引-B+树索引

一、引入索引 在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,因为要遍历所有的数据页,时间复杂度就是O(n),所以这种方式显然是超级耗时的。所以我们需要采取一定的数据结构来存储数据,方便我们进行数据的增删改

[转帖]深入理解mysql-第五章 InnoDB记录存储结构-页结构

前言: 页是InnoDB管理存储空间的基本单位,上一章我们主要分析了页中的主要的构成行的存储结构-行格式,其中简单提了一下页的概念。这章我们详细讲解一下页的存储结构。 一、数据页结构 前边我们简单提了一下页的概念,它是InnoDB管理存储空间的基本单位,一个页的大小一般是16KB。和存储一条条数据的

[转帖]Redis 运维实战 第01期:Redis 复制

https://cloud.tencent.com/developer/article/1986816 作者简介 马听,多年 DBA 实战经验,对 MySQL、 Redis、ClickHouse 等数据库有一定了解,专栏《一线数据库工程师带你深入理解 MySQL》作者。 从这篇文章开始,将出几期 R

[转帖]MySQL 慢查询日志深入理解

https://www.jb51.net/article/210312.htm + 目录 什么是慢查询日志 MySQL的慢查询日志是 MySQL提供的一种日志记录,它用来记录在 MySQL 中响应时间超过阀值的语句,具体指运行时间超过long_query_time 值的 SQL,则会被记录到慢查询日

[转帖]Intel PAUSE指令变化如何影响MySQL的性能

https://zhuanlan.zhihu.com/p/581200704 导读 x86、arm指令都很多,无论是应用程序员还是数据库内核研发大多时候都不需要对这些指令深入理解,但是 Pause 指令和数据库操作太紧密了,本文通过一次非常有趣的性能优化来引入对 Pause 指令的理解,期望可以事半

【转帖】mysql一个索引块有多少指针_深刻理解MySQL系列之索引

索引 查找一条数据的过程 先看下InnoDB的逻辑存储结构:node 表空间:能够看作是InnoDB存储引擎逻辑结构的最高层,全部的数据都存放在表空间中。默认有个共享表空间ibdata1。若是启用innodb_file_per_table参数,须要注意每张表的表空间内存放的只是数据、索引和插入缓冲B

[转帖]深入理解同步机制---内核自旋锁

https://switch-router.gitee.io/blog/spinlock/ 进程(线程)间的同步机制是面试时的常见问题,所以准备用一个系列来好好整理下用户态与内核态的各种同步机制。本文就以内核空间的一种基础同步机制—自旋锁开始好了 自旋锁是什么 自旋锁就是一个二状态的原子(atomi