mysql 优化利器 EXPLAIN (二)

EXPLAIN 介绍

先从一个最简单的查询开始:

Query-1:explain select zipcode,firstname,lastname from people;

mysql> explain select zipcode,firstname,lastname from people;
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | people | index | NULL          | zipcode | 480     | NULL |    3 | Using index |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+

EXPLAIN输出结果共有id,select_type,table,type,possible_keys,key,key_len,ref,rows和Extra几列。

id
Query-2:explain select zipcode from (select * from people a) b;

+----+-------------+------------+------+---------------+------+---------+------+------+-------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
|  1 | PRIMARY     |  | ALL  | NULL          | NULL | NULL    | NULL |    3 | NULL  |
|  2 | DERIVED     | a          | ALL  | NULL          | NULL | NULL    | NULL |    3 | NULL  |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
2 rows in set (0.00 sec)

id是用来顺序标识整个查询中SELELCT 语句的,通过上面这个简单的嵌套查询可以看到id越大的语句越先执行。该值可能为NULL,如果这一行用来说明的是其他行的联合结果,比如UNION语句:

Query-3:explain select * from people where zipcode = 100000 union select * from people where zipcode = 200000;

+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra           |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
|  1 | PRIMARY      | people     | ALL  | zipcode       | NULL | NULL    | NULL |    3 | Using where     |
|  2 | UNION        | people     | ALL  | zipcode       | NULL | NULL    | NULL |    3 | Using where     |
| NULL | UNION RESULT |  | ALL  | NULL          | NULL | NULL    | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
3 rows in set (0.00 sec)
select_type

SELECT语句的类型,可以有下面几种。

SIMPLE

最简单的SELECT查询,没有使用UNION或子查询。见Query-1。

PRIMARY

在嵌套的查询中是最外层的SELECT语句,在UNION查询中是最前面的SELECT语句。见Query-2和Query-3。

UNION

UNION中第二个以及后面的SELECT语句。 见Query-3。

DERIVED

派生表SELECT语句中FROM子句中的SELECT语句。见Query-2。

UNION RESULT

一个UNION查询的结果。见Query-3。

DEPENDENT UNION

顾名思义,首先需要满足UNION的条件,及UNION中第二个以及后面的SELECT语句,同时该语句依赖外部的查询。

Query-4:explain select * from people where id in (select id from people where zipcode = 100000 union select id from people where zipcode = 200000 );

+----+--------------------+------------+--------+-----------------+---------+---------+------+------+-----------------+
| id | select_type        | table      | type   | possible_keys   | key     | key_len | ref  | rows | Extra           |
+----+--------------------+------------+--------+-----------------+---------+---------+------+------+-----------------+
|  1 | PRIMARY            | people     | ALL    | NULL            | NULL    | NULL    | NULL |    3 | Using where     |
|  2 | DEPENDENT SUBQUERY | people     | eq_ref | PRIMARY,zipcode | PRIMARY | 8       | func |    1 | Using where     |
|  3 | DEPENDENT UNION    | people     | eq_ref | PRIMARY,zipcode | PRIMARY | 8       | func |    1 | Using where     |
| NULL | UNION RESULT       |  | ALL    | NULL            | NULL    | NULL    | NULL | NULL | Using temporary |
+----+--------------------+------------+--------+-----------------+---------+---------+------+------+-----------------+
4 rows in set (0.00 sec)

Query-4中select id from people where zipcode = 200000的select_type为DEPENDENT UNION。

你也许很奇怪这条语句并没有依赖外部的查询啊。

这里顺带说下MySQL优化器对IN操作符的优化,优化器会将IN中的uncorrelated subquery优化成一个correlated subquery(关于correlated subquery参见这里)。

SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2); 类似这样的不相关子查询语句会被重写成这样:

SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);
Query-5:explain select * from people o where exists (select id from people where zipcode = 100000 and id = o.id union select id from people where zipcode = 200000 and id = o.id);

+----+--------------------+------------+--------+-----------------+---------+---------+-----------+------+-----------------+
| id | select_type        | table      | type   | possible_keys   | key     | key_len | ref       | rows | Extra           |
+----+--------------------+------------+--------+-----------------+---------+---------+-----------+------+-----------------+
|  1 | PRIMARY            | o          | ALL    | NULL            | NULL    | NULL    | NULL      |    3 | Using where     |
|  2 | DEPENDENT SUBQUERY | people     | eq_ref | PRIMARY,zipcode | PRIMARY | 8       | test.o.id |    1 | Using where     |
|  3 | DEPENDENT UNION    | people     | eq_ref | PRIMARY,zipcode | PRIMARY | 8       | test.o.id |    1 | Using where     |
| NULL | UNION RESULT       |  | ALL    | NULL            | NULL    | NULL    | NULL      | NULL | Using temporary |
+----+--------------------+------------+--------+-----------------+---------+---------+-----------+------+-----------------+
4 rows in set (0.00 sec)

题外话:有时候MySQL优化器这种太过“聪明” 的做法会导致WHERE条件包含IN()的子查询语句性能有很大损失。可以参看《高性能MySQL第三版》6.5.1关联子查询一节。

SUBQUERY

子查询中第一个SELECT语句。

Query-6:explain select * from people where id = (select id from people where zipcode = 100000);

+----+-------------+--------+-------+---------------+---------+---------+-------+------+--------------------------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref   | rows | Extra                    |
+----+-------------+--------+-------+---------------+---------+---------+-------+------+--------------------------+
|  1 | PRIMARY     | people | const | PRIMARY       | PRIMARY | 8       | const |    1 | NULL                     |
|  2 | SUBQUERY    | people | index | zipcode       | zipcode | 480     | NULL  |    3 | Using where; Using index |
+----+-------------+--------+-------+---------------+---------+---------+-------+------+--------------------------+
2 rows in set (0.00 sec)
DEPENDENT SUBQUERY

和DEPENDENT UNION相对UNION一样。见Query-5。

除了上述几种常见的select_type之外还有一些其他的这里就不一一介绍了,不同MySQL版本也不尽相同。

table

显示的这一行信息是关于哪一张表的。有时候并不是真正的表名。

Query-7:explain select * from (select * from (select * from people a) b ) c;

+----+-------------+------------+------+---------------+------+---------+------+------+-------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
|  1 | PRIMARY     |  | ALL  | NULL          | NULL | NULL    | NULL |    3 | NULL  |
|  2 | DERIVED     |  | ALL  | NULL          | NULL | NULL    | NULL |    3 | NULL  |
|  3 | DERIVED     | a          | ALL  | NULL          | NULL | NULL    | NULL |    3 | NULL  |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
3 rows in set (0.00 sec)

可以看到如果指定了别名就显示的别名。

N就是id值,指该id值对应的那一步操作的结果。

还有这种类型,出现在UNION语句中,见Query-4。

注意:MySQL对待这些表和普通表一样,但是这些“临时表”是没有任何索引的。

联系我们

邮箱 626512443@qq.com
电话 18611320371(微信)
QQ群 235681453

Copyright © 2015-2024

备案号:京ICP备15003423号-3