之前的文章中对于explain的数据结果中的字段已经进行了一部分介绍了,今天来说一说剩下的几个字段,为了防止忘记,先看看这个表结构:
mysql--dba_admin@127.0.0.1:yeyztest 21:13:04>>show create table test_explain\G
*************************** 1. row ***************************
Table: test_explain
Create Table: CREATE TABLE `test_explain` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a_key_var` varchar(100) DEFAULT NULL,
`b_key_int` int(11) DEFAULT NULL,
`c_key_var` varchar(100) DEFAULT NULL,
`d_key_var1` varchar(100) DEFAULT NULL,
`d_key_var2` varchar(100) DEFAULT NULL,
`d_key_var3` varchar(100) DEFAULT NULL,
`e_normal_var` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_key_b` (`b_key_int`),
KEY `idx_key_a` (`a_key_var`),
KEY `idx_key_c` (`c_key_var`),
KEY `idx_key_d` (`d_key_var1`,`d_key_var2`,`d_key_var3`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
先来看看最后几个字段是什么:
mysql:yeyztest 21:13:15>>explain select * from test_explain where id=2;
+----+-------------+--------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_explain | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+--------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
可以看到,分别是key_len,ref,rows,filtered以及extra这5个字段。下面我们介绍以下这5个字段的意思:
key_len
key_len表示当优化器制定某个列作为索引的时候,这个索引记录的最大长度,这里需要注意的是,这个最大长度的单位为字节,一般来讲,最大长度由以下三个因素决定:
1、如果该字段是固定长度的类型,例如char类型的,那么它实际占用存储空间的最大长度就是这个固定值。如果是varchar这种变长类型的,那么它的最大长度就是变长类型定义中的长度,比如对于varchar(20),采用utf8编码,最大长度就是20*3=60字节
2、如果索引列中可能包含null值,那么会额外占用1个字节
3、对于varchar这种变长字段,需要有额外的2个字节来保存长度
有了这三条规则,就能比较容易理解key_len的值了,例如上面的例子中,key_len的值是4,它的原因是int类型是固定长度,一个int是4个字节大小
再来看下面这个查询:
mysql:yeyztest 21:14:46>>explain select * from test_explain where a_key_var='a';
+----+-------------+--------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_explain | NULL | ref | idx_key_a | idx_key_a | 303 | const | 1 | 100.00 | NULL |
+----+-------------+--------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
查询的字段是a_key_var,这个字段的类型是varchar(100),那么key_len真实的长度就是100*3+1+2,也就是303了。目前,我们这个字段a_key_var是可以为null值的,如果我们改成不能为null值,如下,可以看到key_len的值发生了变化,变成了302,这就说明了null值是占用了一个字节。
mysql:yeyztest 21:30:40>>alter table test_explain modify a_key_var varchar(100) not null default '';
Query OK, 9 rows affected (0.03 sec)
Records: 9 Duplicates: 0 Warnings: 0
mysql:yeyztest 22:15:09>>explain select * from test_explain where a_key_var='a';
+----+-------------+--------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_explain | NULL | ref | idx_key_a | idx_key_a | 302 | const | 1 | 100.00 | NULL |
+----+-------------+--------------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
ref
ref的值就是指在进行条件过滤的时候,与条件匹配的值是一个常数还是一个变量之类的,我们可以看到,上面的结果中,ref字段的值都是const,是因为我们使用常量a或者常量2和索引字段进行匹配,如果我们使用某个字段进行匹配,来看下面:
mysql:yeyztest 22:24:42>>explain select * from test_explain t1 inner join test2 t2 where t1.id=t2.id;
+----+-------------+-------+------------+--------+---------------+-----------+---------+----------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+-----------+---------+----------------+------+----------+-------------+
| 1 | SIMPLE | t2 | NULL | index | PRIMARY | idx_birth | 6 | NULL | 1 | 100.00 | Using index |
| 1 | SIMPLE | t1 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | yeyztest.t2.id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+-----------+---------+----------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
这个例子中ref的值是yeyztest.t2.id,说明是t1的主键和这个id去做的匹配。
rows
rows代表的是执行计划预计扫描的索引记录行数。上面的例子中,我们可以看到,预计将会扫描的记录数为1条,但是需要注意的是,这个值是个预计值,而不能被看做真实值。
filter
这个值代表的是根据某个索引查询到记录数之后,大约有多少比例的记录匹配剩余的条件。也就是说,比如我们某个表的主键是id,过滤条件是id>4 and age<30,假设id>4的记录数预计为40条,也就是explain中的rows的记录为40,那么filter的意思就是在这40条记录里面,大概有多少比率的记录符合age<30.如下:
mysql:yeyztest 22:40:36>>explain select * from test_1 where id>=4 and age<30;
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | test_1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 40 | 33.33 | Using where |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Extra
这个字段主要还是提供一些查询的额外信息,可能出现的情况比较多,这里列出来常用的一些即可,就不进行举例说明了。
Using where 表示Mysql将对storage engine提取的结果进行过滤,过滤条件字段无索引;
Using temporary 要解决查询,MySQL需要创建一个临时表来保存结果。如果查询包含不同列的GROUP BY和ORDER BY子句,则通常会发生这种情况
Using index 仅使用索引树中的信息从表中检索列信息,而不需要进行回表来读取实际行
Using index condition Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行;
Using filesort 当Query 中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。数据较少时从内存排序,否则从磁盘排序
no matching ... && not exist
找不到匹配的记录。
Using join buffer
这种情况主要发生在join的连接查询中,将外层循环的行/结果集存入join buffer, 内层循环的每一行与整个buffer中的记录做比较,从而减少内层循环的次数