explain就好比是一个化验单,我们在领取了这个化验单之后就需要根据化验单来优化自身。
这里的explain用来select的性能分析,使用方法,在select语句前加上explain就可以了。像下面这个样子,应该一目了然。这样一个查询语句的化验单就出来了,我们这时候就要根据不同的字段来理解这个化验单最终评估这个查询语句的性能。
来看看每个字段的含义吧
id
id可以看成查询的优先级。
id越大,那么越优先查询。同样id查询顺序由上往下。像子查询就是优先子查询。
select_type
这个顾名思义,就是表示查询的类型。分为很多种的。
1.UNION:
当通过union来连接多个查询结果时,第二个之后的select其select_type为UNION。
2.DEPENDENT UNION与DEPENDENT SUBQUERY:
当union作为子查询时,其中第二个union的select_type就是DEPENDENT UNION。
第一个子查询的select_type则是DEPENDENT SUBQUERY。
3.SUBQUERY
子查询中的第一个select其select_type为SUBQUERY。
4.DERIVED
当子查询是from子句或者select字句时,其select_type为DERIVED。
- mysql> explain select * from (select order_id from t_order where order_id=100) a;
- +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+
- | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
- | 2 | DERIVED | t_order | const | PRIMARY | PRIMARY | 4 | | 1 | Using index |
- +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+
- 2 rows in set (0.03 sec)
- SIMPLE:简单SELECT(不使用UNION或子查询)
- PRIMARY:最外面的SELECT
- UNION RESULT:UNION 的结果
table
取决于哪个表
partitions
暂且不知道mysql里面的partitons机制是啥,以后研究了补上
type
联接类型。下面给出各种联接类型,按照从最佳类型到最坏类型进行排序:
- system:表仅有一行(=系统表)。这是const联接类型的一个特例。
- const:表最多有一个匹配行,它将在查询开始时被读取。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次!
- eq_ref:对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。
- 在a表中的id是主键,b表中的id也是主键,该表可以认为是订单表的补充信息表,他们的关系是1对1,在下面的例子中可以看到b表的连接类型是eq_ref,这是极快的联接类型。
- ref:对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。与上面例子的区别就是多了一个条件
- 注意这里id如果只有一条,就是const,因为id已经写死了,所以如果id有重复的,也就是由id=1/G返回来的结果有很多行,所以位ref。
- ref_or_null:该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。
- 在下面的查询中可以看到联接类型为ref_or_null,这是mysql为含有null的字段专门做的处理。在我们的表设计中应当尽量避免索引字段为NULL,因为这会额外的耗费mysql的处理时间来做优化。
- 在上面这个例子中,在给name创建索引之前,是全表查询,即ALL,但是在给name字段创建索引了后,type就变成了ref_or_null。
- index_merge:该联接类型表示使用了索引合并优化方法。经常出现在使用一张表中的多个索引时。mysql会将多个索引合并在一起
- unique_subquery:该类型替换了下面形式的IN子查询的ref: value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。
- index_subquery:该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引: value IN (SELECT key_column FROM single_table WHERE some_expr)
- range:只检索给定范围的行,使用一个索引来选择行。
- index:该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。
- ALL:对于每个来自于先前的表的行组合,进行完整的表扫描。这种情况一般是没有索引
possible_keys
指出MySQL能使用哪个索引在该表中找到行
key
显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL。
key_len
显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。
ref
显示使用哪个列或常数与key一起从表中选择行。
rows
显示MySQL认为它执行查询时必须检查的行数。多行之间的数据相乘可以估算要处理的行数。
fitered
显示了通过条件过滤出的行数的百分比估计值。
Extra
该列包含MySQL解决查询的详细信息
- Distinct:MySQL发现第1个匹配行后,停止为当前的行组合搜索更多的行。
- Not exists:MySQL能够对查询进行LEFT JOIN优化,发现1个匹配LEFT JOIN标准的行后,不再为前面的的行组合在该表内检查更多的行。
-
因为b表中的order_id是主键,不可能为NULL,所以mysql在用a表的order_id扫描t_order表,并查找b表的行时,如果在b表发现一个匹配的行就不再继续扫描b了,因为b表中的order_id字段不可能为NULL。这样避免了对b表的多次扫描。 mysql> explain select count(1) from t_order a left join t_order_ext b on a.order_id=b.order_id where b.order_id is null; +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ | 1 | SIMPLE | a | index | NULL | express_type | 1 | NULL | 100395 | Using index | | 1 | SIMPLE | b | ref | order_id | order_id | 4 | test.a.order_id | 1 | Using where; Using index; Not exists | +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ 2 rows in set (0.01 sec)
- range checked for each record (index map: #):MySQL没有发现好的可以使用的索引,但发现如果来自前面的表的列值已知,可能部分索引可以使用。
-
mysql> explain select * from t_order t, t_order_ext s where s.order_id>=t.order_id and s.order_id<=t.order_id and t.express_type>5; +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ | 1 | SIMPLE | t | range | PRIMARY,express_type | express_type | 1 | NULL | 1 | Using where | | 1 | SIMPLE | s | ALL | order_id | NULL | NULL | NULL | 1 | Range checked for each record (index map: 0x1) | +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ 2 rows in set (0.00 sec)
- Using filesort:MySQL需要额外的一次传递,以找出如何按排序顺序检索行。
-
在有排序子句的情况下很常见的一种情况。此时mysql会根据联接类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行。 mysql> explain select * from t_order order by express_type; +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100395 | Using filesort | +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ 1 row in set (0.00 sec)
- Using index:从只使用索引树中的信息而不需要进一步搜索读取实际的行来检索表中的列信息。
- Using temporary:为了解决查询,MySQL需要创建一个临时表来容纳结果。
- Using where:WHERE 子句用于限制哪一个行匹配下一个表或发送到客户。
- Using sort_union(...), Using union(...), Using intersect(...):这些函数说明如何为index_merge联接类型合并索引扫描。
-
下面的例子中user_id是一个检索范围,此时mysql会使用sort_union函数来进行索引的合并。而当user_id是一个固定值时,请参看上面type说明5.index_merge的例子,此时会使用union函数进行索引合并。 mysql> explain select * from t_order where order_id=100 or user_id>10; +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ | 1 | SIMPLE | t_order | index_merge | PRIMARY,user_id | user_id,PRIMARY | 5,4 | NULL | 2 | Using sort_union(user_id,PRIMARY); Using where | +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ 1 row in set (0.00 sec) 对于Using intersect的例子可以参看下例,user_id与express_type发生了索引交叉合并。 mysql> explain select * from t_order where express_type=1 and user_id=100; +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ | 1 | SIMPLE | t_order | index_merge | user_id,express_type | user_id,express_type | 5,1 | NULL | 1 | Using intersect(user_id,express_type); Using where | +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ 1 row in set (0.00 sec)
- Using index for group-by:类似于访问表的Using index方式,Using index for group-by表示MySQL发现了一个索引,可以用来查 询GROUP BY或DISTINCT查询的所有列,而不要额外搜索硬盘访问实际的表。
表明可以在索引中找到分组所需的所有数据,不需要查询实际的表。
mysql> explain select user_id from t_order group by user_id;
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | t_order | range | NULL | user_id | 5 | NULL | 3 | Using index for group-by |
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
除了上面的三个说明,还需要注意rows的数值,多行之间的数值是乘积的关系,可以估算大概要处理的行数,如果乘积很大,那就很有优化的必要了。