[转帖]深入理解mysql-第十章 mysql查询优化-Explain 详解(上)
目录
一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划,这个执行计划展示了接下来具体执行查询的方式,比如多表连接的顺序是什么,对于每个表采用什么访问方法来具体执行查询等等。Mysql为我们提供了EXPLAIN语句来帮助我们查看某个查询语句的具体执行计划,从而可以有针对性的提升我们查询语句的性能。
一、初识Explain
如果我们想看看某个查询的执行计划的话,可以在具体的查询语句前边加一个EXPLAIN,就像这样:
-
mysql> EXPLAIN SELECT 1;
-
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
-
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
-
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
-
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
-
1 row in set, 1 warning (0.01 sec)
然后这输出的这个表格就是所谓的执行计划,其实除了以SELECT开头的查询语句,其余的DELETE、INSERT、REPLACE以及UPDATE语句前边都可以加上EXPLAIN这个词儿,用来查看这些语句的执行计划,不过我们一般只会对查询语句的性能进行优化。我们把EXPLAIN语句输出的各个列的作用先大致罗列一下:
| 列名 | 描述 |
id |
在一个大的查询语句中每个SELECT关键字都对应一个唯一的id |
select_type |
SELECT关键字对应的那个查询的类型 |
table |
表名 |
partitions |
匹配的分区信息 |
type |
针对单表的访问方法 |
possible_keys |
可能用到的索引 |
key |
实际上使用的索引 |
key_len |
实际使用到的索引长度 |
ref |
当使用索引列等值查询时,与索引列进行等值匹配的对象信息 |
rows |
预估的需要读取的记录条数 |
filtered |
某个表经过搜索条件过滤后剩余记录条数的百分比 |
Extra |
一些额外的信息 |
下面我们将按照这个建表语句,新建s1,s2表,除id列外其余的列都插入随机值:
-
CREATE TABLE single_table (
-
id INT NOT NULL AUTO_INCREMENT,
-
key1 VARCHAR(100),
-
key2 INT,
-
key3 VARCHAR(100),
-
key_part1 VARCHAR(100),
-
key_part2 VARCHAR(100),
-
key_part3 VARCHAR(100),
-
common_field VARCHAR(100),
-
PRIMARY KEY (id),
-
KEY idx_key1 (key1),
-
UNIQUE KEY idx_key2 (key2),
-
KEY idx_key3 (key3),
-
KEY idx_key_part(key_part1, key_part2, key_part3)
-
) Engine=InnoDB CHARSET=utf8;
建完表以后,我们后面开始对其执行计划的每个具体属性进行讲解,不过不会严格按照上面执行计划的展示顺序。
二、执行计划-table属性
不论我们的查询语句有多复杂,里边儿包含了多少个表,到最后也是需要对每个表进行单表访问的,所以设计MySQL的大叔规定EXPLAIN语句输出的每条记录都对应着某个单表的访问方法,该条记录的table列代表着该表的表名。
我们看一下一个连接查询的执行计划:
-
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
-
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
-
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
-
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
-
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | Using join buffer (Block Nested Loop) |
-
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
-
2 rows in set, 1 warning (0.01 sec)
可以看到这个连接查询的执行计划中有两条记录,这两条记录的table列分别是s1和s2,这两条记录用来分别说明对s1表和s2表的访问方法是什么。
三、执行计划-id属性
我们知道我们写的查询语句一般都以SELECT关键字开头,这里id代表的就是这个select的id,比较简单的查询语句里只有一个SELECT关键字,比如单表查询和连表查询。对于连接查询来说,一个SELECT关键字后边的FROM子句中可以跟随多个表,所以在连接查询的执行计划中,每个表都会对应一条记录,但是这些记录的id值都是相同的,可以看第二节的示例。
针对查询中包含子查询和UNION的SQL语句,每个SELECT关键字都会对应一个唯一的id值,所以会有多个id:
-
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
-
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
-
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
-
| 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9688 | 100.00 | Using where |
-
| 2 | SUBQUERY | s2 | NULL | index | idx_key1 | idx_key1 | 303 | NULL | 9954 | 100.00 | Using index |
-
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
-
2 rows in set, 1 warning (0.02 sec)
但是这里大家需要特别注意,并不是所有包含子查询的都会是多个id。这是因为,查询优化器可能对涉及子查询的查询语句进行重写,从而转换为连接查询(上一章的内容)。所以如果我们想知道查询优化器对某个包含子查询的语句是否进行了重写,直接查看执行计划就好了,比如说:
-
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2 WHERE common_field = 'a');
-
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
-
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
-
| 1 | SIMPLE | s2 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9954 | 10.00 | Using where; Start temporary |
-
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | xiaohaizi.s2.key3 | 1 | 100.00 | End temporary |
-
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
-
2 rows in set, 1 warning (0.00 sec)
可以看到,虽然我们的查询语句是一个子查询,但是执行计划中s1和s2表对应的记录的id值全部是1,这就表明了查询优化器将子查询转换为了连接查询。
对于包含UNION子句的查询语句来说,每个SELECT关键字对应一个id值也是没错的,不过还是有点儿特别的东西,比方说下边这个查询:
-
mysql> EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
-
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
-
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
-
| 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
-
| 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | NULL |
-
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
-
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
-
3 rows in set, 1 warning (0.00 sec)
这里UNION会把多个查询的结果集合并起来生成内部的临时表,并对结果集中的记录进行去重,所以在内部创建了一个名为<union1, 2>的临时表。当然UNION ALL就不会生成这个临时表。
四、执行计划-select_type属性
每一个SELECT关键字代表的小查询都定义了一个称之为select_type的属性,实际就是表示了小查询在整个大查询中扮演了一个什么角色。select_type的取值主要包含以下几种:
| 名称 | 描述 |
| SIMPLE | 查询语句中不包含UNION、UNION ALL或者子查询的查询都是SIMPLE类型,当然,连接查询也算是SIMPLE类型。 |
| PRIMARY | 对于包含UNION、UNION ALL或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询的select_type值就是PRIMARY。 |
| UNION | 对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION。 |
| UNION RESULT | MySQL选择使用临时表来完成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-第十章 mysql查询优化-Explain 详解(上)的更多相关文章
- MySQL慢查询优化 EXPLAIN详解
我们平台过一段时间就会把生产数据库的慢查询导出来分析,要嘛修改写法,要嘛新增索引.以下是一些笔记.总结整理 慢查询排查 show status; // 查询mysql ...
- Mysql Explain 详解
Mysql Explain 详解[强烈推荐] Mysql Explain 详解一.语法explain < table_name >例如: explain select * from t3 ...
- MySQL 执行计划explain详解
MySQL 执行计划explain详解 2015-08-10 13:56:27 分类: MySQL explain命令是查看查询优化器如何决定执行查询的主要方法.这个功能有局限性,并不总会说出真相,但 ...
- (转)mysql explain详解
原文:http://www.cnblogs.com/xuanzhi201111/p/4175635.html http://yutonger.com/18.html http://www.jiansh ...
- MYSQL之数据库初识、安装详解、sql语句基本操作
目录 MYSQL之数据库初识及安装详解 1.什么是数据库? 1.什么是数据?(data) 2.什么是数据库?(databases,简称DB) 2.为什要用数据库? 3.什么是数据库管理系统?(Data ...
- MySQL的用户密码过期功能详解
MySQL的用户密码过期功能详解 作者:chszs,未经博主允许不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 先说明两个术语. Payment Ca ...
- MySql绿色版配置及使用详解
原文:MySql绿色版配置及使用详解 最近在做项目开发时用到了MySql数据库,在看了一些有关MySql的文章后,很快就上手使用了.在使用的过程中还是出现了一些问题,因为使用的是绿色免安装版的MySq ...
- MySQL数据库使用mysqldump导出数据详解
mysqldump是mysql用于转存储数据库的实用程序.它主要产生一个SQL脚本,其中包含从头重新创建数据库所必需的命令CREATE TABLE INSERT等.接下来通过本文给大家介绍MySQL数 ...
- 【转】MySQL用户管理及SQL语句详解
[转]MySQL用户管理及SQL语句详解 1.1 MySQL用户管理 1.1.1 用户的定义 用户名+主机域 mysql> select user,host,password from mysq ...
- MySQL数据库的各种存储引擎详解
原文来自:MySQL数据库的各种存储引擎详解 MySQL有多种存储引擎,每种存储引擎有各自的优缺点,大家可以择优选择使用: MyISAM.InnoDB.MERGE.MEMORY(HEAP).BDB ...
随机推荐
- vue获取公网ip
<template> <div> <div>Your Public IP: {{publicIP}}</div> </div> </t ...
- CUDA C编程权威指南:1.2-CUDA基础知识点梳理
主要整理了N多年前(2013年)学习CUDA的时候开始总结的知识点,好长时间不写CUDA代码了,现在LLM推理需要重新学习CUDA编程,看来出来混迟早要还的. 1.闭扫描和开扫描 对于一个二元 ...
- 增长黑客招聘条件 JD
HubSpot招聘T型营销人员加入我们的营销团队.担任此职务后,您将成为第二个致力于HubSpot正在构建的新产品的营销人员.由于其高度机密,我们无法告诉您该产品是什么. 我们正在寻找符合以下条件的人 ...
- 实时媒体AI,打破内容创作天花板,加速视频创新
摘要:互联网视频经过几代发展,用户体验快速升级,对创新玩法.互动性要求越来越高.那如何解决这些问题? 媒体AI,是行业共识.华为云提供云原生实时媒体AI能力,联合伙伴打造AI算法开放生态市场,加速视频 ...
- 为什么vacuum后表还是继续膨胀?
摘要: 对于更新和删除操作频繁的表,会存在大量垃圾数据,导致磁盘空间的浪费和查询扫描时额外的IO开销,需要定期执行清理操作(vacuum)来控制行存表以及表上索引的膨胀.本文将对vacuum的原理以及 ...
- 云小课 | 网站接入WAF失败怎么办?看这里就够了
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要: 网站接入WAF失 ...
- 火山引擎 DataLeap:如何构建一套完整、易用的数据标准体系
数据标准是数据治理体系中的核心要素之一. 一方面,统一的数据标准可以在复杂的业务场景下,帮助团队对齐数据口径,提升数据在分析.诊断等场景的质量与效率:另一方面,数仓团队与分析师团队也需要沉淀一套敏 ...
- Mac Maven环境变量配置 zsh: command not found: mvn
之前配过环境变量,但是后来打开还是报 zsh: command not found: mvn 需要在运行前先刷下环境变量 source ~/.bash_profile 每次使用前都刷一下比较麻烦,这是 ...
- 如何写个死循环,既不独占线程,又不阻塞UI线程?
如果死循环独占线程,500个死循环要占用500个线程,如果死循环不独占线程,500个死循环,用200个线程也行,用20个线程也行,无非是执行的慢点 这样可以把同步操作改写为异步,并且节省线程占用 问个 ...
- 应届生必读:Java真实项目的开发流程和常用工具
本文出自本人写的书,谢绝转载,更勿抄袭. 本人有多年的Java面试官经验,经常要和一些包装项目经验的求职者打交道.当然平时也兼职做些Java面试辅导工作,最近也陆续帮一些在校生朋友成功找到Java工作 ...