上一篇文章讲了MySQL 索引,本篇文站来讲讲 explain 命令。explain 命令显示了 MySQL 如何使用索引来处理 SELECT 语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。
为了更好的展示,我们先来建两张表,并向这两张表分别插入1000条记录:
CREATE TABLE `s1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`unique_key` varchar(100) DEFAULT NULL,
`normal_key` varchar(100) DEFAULT NULL,
`key_part1` varchar(100) DEFAULT NULL,
`key_part2` varchar(100) DEFAULT NULL,
`memo` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY (`unique_key`),
KEY (`normal_key`),
KEY (`key_part1`,`key_part2`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `s2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`unique_key` varchar(100) DEFAULT NULL,
`normal_key` varchar(100) DEFAULT NULL,
`key_part1` varchar(100) DEFAULT NULL,
`key_part2` varchar(100) DEFAULT NULL,
`memo` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY (`unique_key`),
KEY (`normal_key`),
KEY (`key_part1`,`key_part2`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
执行命令
mysql> EXPLAIN select * from s1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: s1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
可以看到explain 出来的信息,下面分别解释这些字段。
id
在一个大的查询语句中每个SELECT关键字都对应一个唯一的id
, id
越大,优先级越高。
select_type
SELECT关键字对应的那个查询的类型。(一般都是SIMPLE
,这个字段不重要哈)
SIMPLE
(简单SELECT,不使用UNION或子查询等)PRIMARY
(查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)UNION
(UNION中的第二个或后面的SELECT语句)DEPENDENT UNION
(UNION中的第二个或后面的SELECT语句,取决于外面的查询)UNION RESULT
(UNION的结果)SUBQUERY
(子查询中的第一个SELECT)DEPENDENT SUBQUERY
(子查询中的第一个SELECT,取决于外面的查询)DERIVED
(派生表的SELECT, FROM子句的子查询)UNCACHEABLE SUBQUERY
(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)
table
很明显,是表名(这个也不重要哈)
partitions
匹配的分区信息(一般都为NULL,这个还是不重要哈)
type
表示在表中找到所需记录的方式,又称“访问方法”(这个很重要
)。
常用的类型有:system, const, eq_ref, ref, range, index, ALL(一般情况下,从左到右,性能从好到坏。通常要求写的SQL至少>range
)
system
: 当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的
,比如MyISAM、Memory,那么对该表的访问方法就是system。const
:根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是const。
mysql> EXPLAIN SELECT * FROM s1 WHERE id = 11;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM s1 WHERE unique_key = 'test_1_1';
+----+-------------+-------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | const | unique_key | unique_key | 303 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
eq_ref
:在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的。(s1 为驱动表,s2 为被驱动表)
mysql> EXPLAIN SELECT * FROM s1 join s2 on s1.id=s2.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 1000 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | house.s1.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
ref
: 普通的二级索引列与常量进行等值匹配时来查询某个表range
:使用索引获取某些范围区间的记录index
: Full Index Scan,遍历二级索引树(联合索引)。比如下面的查询
mysql> EXPLAIN SELECT key_part1, key_part2 FROM s1 where key_part2='part2_84';
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | s1 | NULL | index | NULL | key_part1 | 606 | NULL | 1000 | 10.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
ALL
: Full Table Scan,全表遍历
possible_keys 和 key
possible_keys
是指可能用到的索引,key
是指实际用到的索引。(这两个很重要
)
key_len
表示当优化器决定使用某个索引执行查询时,该索引记录的最大长度。不损失精确性的情况下,长度越短越好。
ref
当使用索引列等值匹配的条件去执行查询时,展示的就是与索引列作等值匹配是一个常数或者是某个列。比如下述 SQL 的 normal_11
就是一个常量。
EXPLAIN SELECT * FROM s1 WHERE normal_key = 'normal_11';
filtered
一般是100%,是查询优化器的预测值(这个不重要)
rows
表示执行查询预计需要扫描的行数(这个很重要
)
Extra
用来说明一些额外信息的(这个很重要
)
Using where
: 当我们使用全表扫描来执行对某个表的查询,并且该语句的WHERE子句中有针对该表的搜索条件时,在Extra列中会提示上述额外信息。Using temporary
:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询(需要优化
)Using filesort
:表示MySQL中无法利用索引完成的排序操作称为“文件排序”(需要优化
)Impossible where
:这个值强调了where语句
没有符合条件的行Using index
: 当我们的查询列表以及搜索条件中只包含属于某个索引的列,也就是在可以使用索引覆盖的情况下。就是不需要回表。Using index condition
: 有些搜索条件中虽然出现了索引列,但却不能使用到索引。比如下面的SQL,这里涉及一个知识点叫索引条件下推
。可以google下哦
SELECT * FROM s1 WHERE normal_key > 'd' AND normal_key LIKE '%a';