在分析查询性能时,考虑EXPLAIN关键字同样很管用。EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作、以及MySQL成功返回结果集需要执行的行数。explain 可以帮助我们分析 select 语句,让我们知道查询效率低下的原因,从而改进我们查询,让查询优化器能够更好的工作。

一、MySQL 查询优化器是如何工作的MySQL 查询优化器有几个目标,但是其中最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行。最终目标是提交 SELECT 语句查找数据行,而不是排除数据行。优化器试图排除数据行的原因在于它排除数据行的速度越快,那么找到与条件匹配的数据行也就越快。如果能够首先进行最严格的测试,查询就可以执行地更快。

EXPLAIN 的每个输出行提供一个表的相关信息,并且每个行包括下面的列:

说明

id

MySQL Query Optimizer 选定的执行计划中查询的序列号。表示查询中执行 select 子句或操作表的顺序,id 值越大优先级越高,越先被执行。id 相同,执行顺序由上至下。

select_type 查询类型

说明

SIMPLE

简单的 select 查询,不使用 union 及子查询

PRIMARY

最外层的 select 查询

UNION

UNION 中的第二个或随后的 select 查询,不 依赖于外部查询的结果集

DEPENDENT UNION

UNION 中的第二个或随后的 select 查询,依 赖于外部查询的结果集

SUBQUERY

子查询中的第一个 select 查询,不依赖于外 部查询的结果集

DEPENDENT SUBQUERY

子查询中的第一个 select 查询,依赖于外部 查询的结果集

DERIVED

用于 from 子句里有子查询的情况。 MySQL 会 递归执行这些子查询, 把结果放在临时表里。

UNCACHEABLE SUBQUERY

结果集不能被缓存的子查询,必须重新为外 层查询的每一行进行评估。

UNCACHEABLE UNION

UNION 中的第二个或随后的 select 查询,属 于不可缓存的子查询

说明

table

输出行所引用的表

type 重要的项,显示连接使用的类型,按最 优到最差的类型排序

说明

system

表仅有一行(=系统表)。这是 const 连接类型的一个特例。

const

const 用于用常数值比较 PRIMARY KEY 时。当 查询的表仅有一行时,使用 System。

eq_ref

One row is read from this table for each combination of rows from the previous tables. Other than thesystemandconsttypes,this is the best possible join type. It is used when all parts of an index are used by the join and the index is aPRIMARY KEYorUNIQUE NOT NULLindex.

SELECT * FROM ref_table,other_table

WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table

WHERE ref_table.key_column_part1=other_table.column

AND ref_table.key_column_part2=1;

ref

All rows with matching index values are read from this table for each combination of rows from the previous tables. ref is used if the join uses only a leftmost prefix of the key or if the key is not a PRIMARY KEY or UNIQUE index (in other words, if the join cannot select a single row based on the key value). If the key that is used matches only a few rows, this is a good join type.

ref can be used for indexed columns that are compared using the = or <=> operator. In the following examples, MySQL can use a ref join to process ref_table:

SELECT * FROM ref_table WHERE key_column=expr;

SELECT * FROM ref_table,other_table

WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table

WHERE ref_table.key_column_part1=other_table.column

AND ref_table.key_column_part2=1;

ref_or_null

This join type is likeref, but with the addition that MySQL does an extra search for rows that containNULLvalues. This join type optimization is used most often in resolving subqueries. In the following examples, MySQL can use aref_or_nulljoin to processref_table:

SELECT * FROM ref_table

WHERE key_column=expr OR key_column IS NULL;

index_merge

说明索引合并优化被使用了。

unique_subquery

This type replaces ref for some IN subqueries of the following form:

value IN (SELECT primary_key FROM single_table WHERE some_expr)

unique_subquery is just an index lookup function that replaces the subquery completely for better efficiency.

index_subquery

This join type is similar to unique_subquery. It replaces IN subqueries, but it works for nonunique indexes in subqueries of the following form:

value IN (SELECT key_column FROM single_table WHERE some_expr)

range

Only rows that are in a given range are retrieved, using an index to select the rows. The key column in the output row indicates which index is used. The key_len contains the longest key part that was used. The ref column is NULL for this type.

range can be used when a key column is compared to a constant using any of the =, <>, >, >=, , BETWEEN, or IN() operators:

SELECT * FROM tbl_name

WHERE key_column = 10;

SELECT * FROM tbl_name

WHERE key_column BETWEEN 10 and 20;

SELECT * FROM tbl_name

WHERE key_column IN (10,20,30);

SELECT * FROM tbl_name

WHERE key_part1 = 10 AND key_part2 IN (10,20,30);

index

The index join type is the same as ALL, except that the index tree is scanned. This occurs two ways:

If the index is a covering index for the queries and can be used to satisfy all data required from the table, only the index tree is scanned. In this case, the Extra column says Using index. An index-only scan usually is faster than ALL because the size of the index usually is smaller than the table data.

A full table scan is performed using reads from the index to look up data rows in index order. Uses index does not appear in the Extra column.

MySQL can use this join type when the query uses only columns that are part of a single index.

all

最坏的情况,从头到尾全表扫描。

说明

possible_keys

指出 MySQL 能在该表中使用哪些索引有助于 查询。如果为空,说明没有可用的索引。

说明

key

MySQL 实际从 possible_key 选择使用的索引。 如果为 NULL,则没有使用索引。很少的情况 下,MYSQL 会选择优化不足的索引。这种情 况下,可以在 SELECT 语句中使用 USE INDEX (indexname)来强制使用一个索引或者用 IGNORE INDEX(indexname)来强制 MYSQL 忽略索引

说明

key_len

使用的索引的长度。在不损失精确性的情况 下,长度越短越好。

说明

ref

显示索引的哪一列被使用了

说明

rows

MYSQL 认为必须检查的用来返回请求数据的行数

说明

rows

MYSQL 认为必须检查的用来返回请求数据的行数

extra 中出现以下 2 项意味着 MYSQL 根本不能使用索引,效率会受到重大影响。应尽可能对此进行优化。

extra 项

说明

Using filesort

表示 MySQL 会对结果使用一个外部索引排序,而不是从表里按索引次序读到相关内容。可能在内存或者磁盘上进行排序。MySQL 中无法利用索引完成的排序操作称为“文件排序”

Using temporary

表示 MySQL 在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。

下面来举一个例子来说明下 explain 的用法。

先来一张表:

复制代码代码如下:

CREATE TABLE IF NOT EXISTS `article` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`author_id` int(10) unsigned NOT NULL,

`category_id` int(10) unsigned NOT NULL,

`views` int(10) unsigned NOT NULL,

`comments` int(10) unsigned NOT NULL,

`title` varbinary(255) NOT NULL,

`content` text NOT NULL,

PRIMARY KEY (`id`)

);

再插几条数据:

复制代码代码如下:

INSERT INTO `article`

(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES

(1, 1, 1, 1, '1', '1'),

(2, 2, 2, 2, '2', '2'),

(1, 1, 3, 3, '3', '3');

需求:查询 category_id 为 1 且 comments 大于 1 的情况下,views 最多的 article_id。先查查试试看:

复制代码代码如下:

EXPLAIN

SELECT author_id

FROM `article`

WHERE category_id = 1 AND comments > 1

ORDER BY views DESC

LIMIT 1\G

看看部分输出结果:

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: article

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 3

Extra: Using where; Using filesort

1 row in set (0.00 sec)

很显然,type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。

嗯,那么最简单的解决方案就是加索引了。好,我们来试一试。查询的条件里即 where 之后共使用了 category_id,comments,views 三个字段。那么来一个联合索引是最简单的了。

复制代码代码如下:

ALTER TABLE `article` ADD INDEX x ( `category_id` , `comments`, `views` );

结果有了一定好转,但仍然很糟糕:

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: article

type: range

possible_keys: x

key: x

key_len: 8

ref: NULL

rows: 1

Extra: Using where; Using filesort

1 row in set (0.00 sec)

type 变成了 range,这是可以忍受的。但是 extra 里使用 Using filesort 仍是无法接受的。但是我们已经建立了索引,为啥没用呢?这是因为按照 BTree 索引的工作原理,先排序 category_id,如果遇到相同的 category_id 则再排序 comments,如果遇到相同的 comments 则再排序 views。当 comments 字段在联合索引里处于中间位置时,因comments > 1 条件是一个范围值(所谓 range),MySQL 无法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。那么我们需要抛弃 comments,删除旧索引:

复制代码代码如下:

DROP INDEX x ON article;

然后建立新索引:

复制代码代码如下:

ALTER TABLE `article` ADD INDEX y ( `category_id` , `views` ) ;

接着再运行查询:

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: article

type: ref

possible_keys: y

key: y

key_len: 4

ref: const

rows: 1

Extra: Using where

1 row in set (0.00 sec)

可以看到,type 变为了 ref,Extra 中的 Using filesort 也消失了,结果非常理想。再来看一个多表查询的例子。首先定义 3个表 class 和 room。

复制代码代码如下:

CREATE TABLE IF NOT EXISTS `class` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`card` int(10) unsigned NOT NULL,

PRIMARY KEY (`id`)

);

CREATE TABLE IF NOT EXISTS `book` (

`bookid` int(10) unsigned NOT NULL AUTO_INCREMENT,

`card` int(10) unsigned NOT NULL,

PRIMARY KEY (`bookid`)

);

CREATE TABLE IF NOT EXISTS `phone` (

`phoneid` int(10) unsigned NOT NULL AUTO_INCREMENT,

`card` int(10) unsigned NOT NULL,

PRIMARY KEY (`phoneid`)

) engine = innodb;

然后再分别插入大量数据。插入数据的php脚本:

复制代码代码如下:

$link = mysql_connect("localhost","root","870516");

mysql_select_db("test",$link);

for($i=0;$i<10000;$i++)

{

$j   = rand(1,20);

$sql = " insert into class(card) values({$j})";

mysql_query($sql);

}

for($i=0;$i<10000;$i++)

{

$j   = rand(1,20);

$sql = " insert into book(card) values({$j})";

mysql_query($sql);

}

for($i=0;$i<10000;$i++)

{

$j   = rand(1,20);

$sql = " insert into phone(card) values({$j})";

mysql_query($sql);

}

mysql_query("COMMIT");

?>

然后来看一个左连接查询:

复制代码代码如下:

explain select * from class left join book on class.card = book.card\G

分析结果是:

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

2 rows in set (0.00 sec)

显然第二个 ALL 是需要我们进行优化的。建立个索引试试看:

复制代码代码如下:

ALTER TABLE `book` ADD INDEX y ( `card`);

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ref

possible_keys: y

key: y

key_len: 4

ref: test.class.card

rows: 1000

Extra:

2 rows in set (0.00 sec)

可以看到第二行的 type 变为了 ref,rows 也变成了 1741*18,优化比较明显。这是由左连接特性决定的。LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有,所以右边是我们的关键点,一定需要建立索引。删除旧索引:

复制代码代码如下:

DROP INDEX y ON book;

建立新索引。

复制代码代码如下:

ALTER TABLE `class` ADD INDEX x ( `card`);

结果

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

2 rows in set (0.00 sec)

基本无变化。然后来看一个右连接查询:

复制代码代码如下:

explain select * from class right join book on class.card = book.card;

分析结果是:

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ref

possible_keys: x

key: x

key_len: 4

ref: test.book.card

rows: 1000

Extra:

2 rows in set (0.00 sec)

优化较明显。这是因为 RIGHT JOIN 条件用于确定如何从左表搜索行,右边一定都有,所以左边是我们的关键点,一定需要建立索引。删除旧索引:

复制代码代码如下:

DROP INDEX x ON class;

建立新索引。

复制代码代码如下:

ALTER TABLE `book` ADD INDEX y ( `card`);

结果

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

2 rows in set (0.00 sec)

基本无变化。

最后来看看 inner join 的情况:

复制代码代码如下:

explain select * from class inner join book on class.card = book.card;

结果:

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ref

possible_keys: x

key: x

key_len: 4

ref: test.book.card

rows: 1000

Extra:

2 rows in set (0.00 sec)

删除旧索引:

复制代码代码如下:

DROP INDEX y ON book;

结果

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

2 rows in set (0.00 sec)

建立新索引。

复制代码代码如下:

ALTER TABLE `class` ADD INDEX x ( `card`);

结果

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

2 rows in set (0.00 sec)

综上所述,inner join 和 left join 差不多,都需要优化右表。而 right join 需要优化左表。

我们再来看看三表查询的例子

添加一个新索引:

复制代码代码如下:

ALTER TABLE `phone` ADD INDEX z ( `card`);

ALTER TABLE `book` ADD INDEX y ( `card`);

复制代码代码如下:

explain select * from class left join book on class.card=book.card left join phone on book.card = phone.card;

复制代码代码如下:

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: class

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20000

Extra:

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: book

type: ref

possible_keys: y

key: y

key_len: 4

ref: test.class.card

rows: 1000

Extra:

*************************** 3. row ***************************

id: 1

select_type: SIMPLE

table: phone

type: ref

possible_keys: z

key: z

key_len: 4

ref: test.book.card

rows: 260

Extra: Using index

3 rows in set (0.00 sec)

后 2 行的 type 都是 ref 且总 rows 优化很好,效果不错。MySql 中的 explain 语法可以帮助我们改写查询,优化表的结构和索引的设置,从而最大地提高查询效率。当然,在大规模数据量时,索引的建立和维护的代价也是很高的,往往需要较长的时间和较大的空间,如果在不同的列组合上建立索引,空间的开销会更大。因此索引最好设置在需要经常查询的字段中。

mysql explain output_MySQL查询优化之explain的深入解析【转载】相关推荐

  1. Mysql性能调优工具Explain结合语句讲解

    Explain简称执行计划,可以模拟SQL语句,来分析查询语句或者表结构是否有性能瓶颈. Explain的作用有哪些,可以看到哪些? 可以看到表的读取顺序,数据读取操作的操作类型,哪些索引可以使用,哪 ...

  2. mysql explain 索引_MySql中Explain详解与索引最佳实践

    使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是 如何处理你的SQL语句的.分析你的查询语句或是结构的性能瓶颈 下面是使用 explain 的例子: 在 select 语句之 ...

  3. MySQL的高级应用之Explain(完美详细版,看这一篇就够了)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/wx1528159409 最近学习MySQL的高级应用Explain,写一篇学习心得与总结,目录脑图如 ...

  4. Mysql——索引底层数据结构与Explain用法

    Mysql--索引底层数据结构与Explain用法 一.索引底层数据结构 1.Mysql不同引擎对应的数据结构 2.B+Tree数据结构 2.1. 二叉树 (Binary Search Trees) ...

  5. MySql数据库explain用法示例_mysql explain的用法

    MySQL的EXPLAIN命令显示了mysql如何使用索引来处理select语句以及连接表.可以帮助选择更好的索引和写出更优化的查询语句. 一.通过expalin可以得到 1.表的读取顺序 2.表的读 ...

  6. mysql的explain的用途,利用Explain来分析和优化你的mysql

    下面我用一个例子来演示如何使用explain来优化mysql查询: 需求是这样的,在一个有1300+万条的mysql表中查出一个时间段内的数据,联表查询:select d2.name as '大区'  ...

  7. mysql range用法_MySQL中Explain的用法总结(详细)

    本篇文章给大家带来的内容是关于MySQL中Explain的用法总结(详细),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 执行计划(query Execution plan) 语法e ...

  8. 最全MySQL8.0实战教程 22 MySQL的优化 22.4 explain分析执行计划 22.4.1 基本使用

    最全MySQL8.0实战教程 文章目录 最全MySQL8.0实战教程 22 MySQL的优化 22.4 explain分析执行计划 22.4.1 基本使用 [黑马程序员MySQL知识精讲+mysql实 ...

  9. explain mysql怎么用_mysql中explain用法详解

    EXPLAIN用于SELECT语句中的每个表返回一行信息.表以它们在处理查询过程中将被MySQL读入的顺序被列出 如果在select语句前放上关键词explain,mysql将解释它如何处理selec ...

最新文章

  1. python sched_python事件调度库sched
  2. python调用数据集mnist_Python读取MNIST数据集
  3. python 框架好学吗-python的flask框架难学吗
  4. linux利用命令重置大量密码
  5. linux上git克隆命令,Git clone命令用法
  6. (二分+区间搜索 )Mountain Walking(poj2110/poj2922)
  7. 运用Logistic模型检验影响企业现金分红的主要因素
  8. uniapp中封装ajax方法
  9. jQuery方式追加div覆盖
  10. 纽约出租车计费问题:一个简单的线性模型
  11. 黑客帝国,社会化网络
  12. 基于Tensorflow的MINIST手写体识别
  13. 51单片机流水灯从原理图到PCB转化
  14. KPI and evaluation decouple verification
  15. 多边形标注收缩python代码实现
  16. 网站点击弹窗微信二维码功能纯CSS
  17. linux执行scp命令出错
  18. 网站调用在线二维码生成 api
  19. 尚硅谷webpack知识点梳理
  20. 2018年杭州马拉松感受

热门文章

  1. 大数据聚类分析用于预测_多模态数据中的非负矩阵分解用于分割和标签预测
  2. 常见花材的固定的方法有哪些_旋流器常见的故障及处理方法有哪些?
  3. MySQL 如何复制表
  4. Linux 命令之 touch -- 创建文件
  5. LeetCode 面试题55 二叉树的深度
  6. java中bpmn流程图_Java学习之BPMN知识以及Activiti的流程部署
  7. 图解python pdf_Python合并同一个文件夹下所有PDF文件的方法
  8. windows系统c 实现ftp服务器,windows系统c 实现ftp服务器
  9. css 样式尾部带感叹号是什么意思_CSS书写规范
  10. docker $PWD路径_Docker安装Jenkins+Shell脚本自动化部署项目