本文使用的样表
mysql> show create table artist\G
*************************** 1. row ***************************
Table: artist
Create Table: CREATE TABLE `artist` (
`a_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
PRIMARY KEY (`a_id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
mysql> show create table painting\G
*************************** 1. row ***************************
Table: painting
Create Table: CREATE TABLE `painting` (
`a_id` int(10) unsigned NOT NULL,
`p_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`state` varchar(2) NOT NULL,
`price` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`p_id`),
KEY `a_id` (`a_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
explain用法
explain用法非常简单,在SQL语句前加上explain就可以,例如:
mysql> explain select*from artist;
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | artist | NULL | index | NULL | name | 122 | NULL | 4 | 100.00 | Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
explain输出的字段含义
- id:SQL执行的序列号,应该是按从大到小的顺序执行的。
- select_type:select的类型,稍后详述。
- table:这一行的数据是关于哪张表的。不一定是真实的表名字。
- type:使用了哪种联接类别,有无使用索引。非常重要,稍后详述。
- partitions:分区。
- possible_keys:可能使用的索引。如果该列是NULL,则没有使用的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。
- key:MySQL实际使用的索引。如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
- key_len:MySQL使用的键长度。如果键是NULL,则长度为NULL。在不损失精确性的情况下,长度越短越好。
- ref:使用哪个列或常数与key一起从表中选择行。
- rows:MySQL认为它执行查询时必须检索的行数。
- Extra:该列包含MySQL解决查询的详细信息。稍后详述。
select_type详细
select_type表示当前查询的类型,可能值如下:
- SIMPLE:简单SELECT(不使用UNION或子查询等)
- PRIMARY:最外层的SELECT
- UNION:UNION中的非PRIMARY的SELECT语句
- DEPENDENT UNION:与UNION相似,不过是依赖外部查询的UNION,性能低
- UNION RESULT:UNION的结果。
- SUBQUERY:子查询中的第一个SELECT。
- DEPENDENT SUBQUERY:与SUBQUERY相似,不过是依赖外部查询的SUBQUERY,性能低
- DERIVED:派生表的SELECT(FROM子句的子查询)
type详细
type从最好到最差的类型如下:
- system
- const
- eq_ref
- ref
- range
- index
- ALL
1.system
system类型是const类型的一个特例。表仅有一行时就是system。
mysql> explain select version();
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 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.00 sec)
2.const
const类型表示最多只有一个匹配行,它将在查询开始时被读取。当条件中用主键列或UNIQUE列进行单值比较时,就是const。
mysql> explain select*from artist where a_id='1';
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | artist | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
3.eq_ref
eq_ref表示等值引用。对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。当条件中用来自两个表的关联字段,并且该字段是UNIQUE或PRIMARY列时,就是eq_ref。
mysql> explain select*from artist,painting where artist.a_id=painting.a_id;
+----+-------------+----------+------------+--------+---------------+---------+---------+------------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+--------+---------------+---------+---------+------------------------+------+----------+-------+
| 1 | SIMPLE | painting | NULL | ALL | a_id | NULL | NULL | NULL | 5 | 100.00 | NULL |
| 1 | SIMPLE | artist | NULL | eq_ref | PRIMARY | PRIMARY | 4 | cookbook.painting.a_id | 1 | 100.00 | NULL |
+----+-------------+----------+------------+--------+---------------+---------+---------+------------------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
或:
mysql> explain select name,title from artist join painting on artist.a_id=painting.a_id;
+----+-------------+----------+------------+--------+---------------+---------+---------+------------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+--------+---------------+---------+---------+------------------------+------+----------+-------+
| 1 | SIMPLE | painting | NULL | ALL | a_id | NULL | NULL | NULL | 5 | 100.00 | NULL |
| 1 | SIMPLE | artist | NULL | eq_ref | PRIMARY | PRIMARY | 4 | cookbook.painting.a_id | 1 | 100.00 | NULL |
+----+-------------+----------+------------+--------+---------------+---------+---------+------------------------+------+----------+-------+
2 rows in set, 1 warning (0.01 sec)
4.ref
ref即引用。对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或PRIMARY KEY(换句话说,如果联接不能基于关键字选择单个行的话),则使用ref。如果使用的键仅仅匹配少量行,该联接类型是不错的。
因为painting表中的a_id只是索引,但不是唯一索引。
mysql> explain select*from painting where a_id=1;
+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+
| 1 | SIMPLE | painting | NULL | ref | a_id | a_id | 4 | const | 2 | 100.00 | NULL |
+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
5.ref_or_null
该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。在解决子查询中经常使用该联接类型的优化。
6.index_merge
该类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。
mysql> explain select*from painting where a_id=1 or p_id=1;
+----+-------------+----------+------------+-------------+---------------+--------------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------------+---------------+--------------+---------+------+------+----------+----------------------------------------+
| 1 | SIMPLE | painting | NULL | index_merge | PRIMARY,a_id | a_id,PRIMARY | 4,4 | NULL | 3 | 100.00 | Using union(a_id,PRIMARY); Using where |
+----+-------------+----------+------------+-------------+---------------+--------------+---------+------+------+----------+----------------------------------------+
1 row in set, 1 warning (0.00 sec)
7.unique_subquery
该类型替换了下面形式的IN子查询的ref:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。
8.index_subquery
该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引:
value IN (SELECT key_column FROM single_table WHERE some_expr)
9.range
只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。key_len包含所使用索引的最长关键元素。在该类型中ref列为NULL。
当使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比较关键字列时,可以使用range
mysql> explain select*from painting where a_id in (1,2,3);
+----+-------------+----------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | painting | NULL | range | a_id | a_id | 4 | NULL | 5 | 100.00 | Using index condition |
+----+-------------+----------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
10.index
该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。当查询只使用作为单索引一部分的列时,MySQL可以使用该联接类型。
mysql> explain select*from artist where a_id is not null;
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | artist | NULL | index | PRIMARY | name | 122 | NULL | 4 | 75.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
11.ALL
对于每个来自于先前的表的行组合,进行完整的表扫描。如果表是第一个没标记const的表,性能很差。通常可以增加更多的索引而不要使用ALL,使得行能基于前面的表中的常数值或列值被检索出。
mysql> explain select*from painting where a_id is not null;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | painting | NULL | ALL | a_id | NULL | NULL | NULL | 5 | 80.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Extra详述
Extra可以是以下组合:
- Distinct:一旦MYSQL找到了与行相联合匹配的行,就不再搜索了
- Not exists:MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了
- Range checked for each
- Record(index map:#)没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
- Using filesort:看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
- Using index:列数据是从仅仅使用了索引中的信息而没有读取实际的行的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
- Using temporary:看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上
- Using where:使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题