简介
本篇文章主要介绍 explain <sql>
输出的结果中各字段的含义。比如我们先用如下的sql插入一些数据:
create table t1(id int(10) auto_increment primary key, content varchar(100) null);
create table t2(id int(10) auto_increment primary key, content varchar(100) null);
create table t3(id int(10) auto_increment primary key, content varchar(100) null);
create table t4(id int(10) auto_increment primary key, content varchar(100) null);
insert into t1(content) values(concat('t1_', floor(1+rand()*100)));
insert into t2(content) values(concat('t1_', floor(1+rand()*100)));
insert into t3(content) values(concat('t1_', floor(1+rand()*100)));
insert into t4(content) values(concat('t1_', floor(1+rand()*100)));
explain select * from t1 where id in (select id from t2);
字段
id
SELECT 标记,当query中出现多个 SELECT 子句时,id的顺序代表了 SELECT 子句的执行顺序。大致分为以下几种情况:
-
id相同,执行顺序由上至下
explain select * from t1, t2, t3 where t1.id=t2.id and t2.id=t3.id;
这个例子里,id都等于1,代表这几个查询的优先级相同,具体执行的查表顺序是t2->t1->t3,这个顺序是由优化器决定的。
-
id不同,从大往小执行
explain select * from t1 where t1.id = (select id from t2 where id = (select id from t3))
这个例子里,先执行id=3位于最里层的子查询
select id from t3
,然后依次向外。 -
id有相同也有不同,不相同的从大往小执行,相同的从上往下执行
explain select * from t1 where t1.id = (select id from t2 where id = (select id from t3)) and t1.id in (select id from t4)
我们在后面追加一个in的子查询,这里的查表顺序是,t3->t2->t1->t4。
select_type
SELECT 类型,有下面几种常见取值
SIMPLE
:最简单的查询,没有联合查询(union)或子查询(subquery)PRIMARY
:查询中若包含任何复杂查询,PRIMARY代表最外层的查询UNION
:联合查询(union)中处于内层的查询UNION RESULT
:从UNION表获取结果的SELECTSUBQUERY
:如果包含了子查询,该子查询被标记为SUBQUERYDERIVED
:在from子句中包含的子查询被标记为DERIVED
table
说明查询是基于那张表的,并不一定是实际的表,比如:
- <union M,N>: 输出结果集是 id=M 和 id=N 两个查询结果集的并集;
explain select t1.* from t1 union select t2.* from t2;
- <derived N>: 输出编号 id=N 的结果集,derived 表示这是一个派生结果集,如 FROM 子句中的查询。
- <subquery N>: 输出编号 id=N 的结果集,subquery 表示这是一个物化子查询(masterialization subquery)
partitions
查询时匹配的分区信息,NULL
说明表没有被分区。
type
查询的访问类型,是影响性能比较重要的指标,有下面几种取值,性能依次从高到低:
-
system
:访问的系统表仅存在一条记录,很特殊的一种情况,一般遇不到 -
const
:当使用主键或唯一索引查询,且查询值是一个常量,这种情况下速度非常快,比如:
explain select * from t1 where id=1;
-
eq_ref
:当使用主键或唯一索引做连表查询(每个索引值仅返回一条记录)比如:explain select * from t1,t2 where t1.id=t2.id;
-
ref
:使用普通索引查询,对每个索引值可以返回多条记录,我们可以给t1.content加上索引,然后查询:
explain select * from t1 where t1.content='t1_12';
-
fulltext
:连表查询使用FULLTEXT
索引 -
ref_or_null
:与ref
类似,只是还需要判断是否为null,比如:
explain select * from t1 where t1.content='t1_12' or t1.content is NULL;
-
index_merge
:索引合并,一般出现在使用or的查询中,比如:
explain select * from t1 where id=3 or seq=1;
-
unique_subquery
:在某些使用唯一索引的in子查询中,替代eq_ref,比如:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
-
index_subquery
:与unique_subquery类似,在in子查询中使用,只是用于非唯一索引,比如:
value IN (SELECT key_column FROM single_table WHERE some_expr)
-
range
:使用索引做范围查询,比如:
explain select * from t1 where t1.id > 3;
-
index
:类似于全表扫描,用到了索引树但没有进行过滤,因为索引树通常体积更小,所以比起全表扫描要快:
explain select id from t1;
-
ALL
:全表扫描,性能最差:
explain select * from t1;
possible_keys
查询时可能用到的索引(实际不一定用到)
key
查询时实际用到的索引,有可能possible_keys是NULL,但key有值,这在用覆盖索引查询时会出现,既无法通过索引过滤掉数据,但通过遍历索引树可以拿到查询要求的列,比如:
explain select id from t1;
key_len
用到的索引字节数,在不损失精度的情况下,用到的长度越短越好。
ref
显示索引的哪一列被使用了,取值有:
- const:当使用常量或等值查询
- func:查询条件使用了函数或发生了内部隐式转换
- 字段名:和key索引进行比较的列
rows
mysql判断为了完成查询,需要搜索的行数,越少越好,这是一个估计值。
filtered
filtered同样是一个估计值,它是一个百分比数(最大100),代表了按照type指定的查询方式,最终返回的行数占搜索行数的百分比。filtered 越大说明根据索引检查的行基本都要返回,这个索引越有效。如果我们有1000条数据,type=ALL,filtered=0.001
,则最终只会有1条记录返回,此时如果能加个索引直接搜出这条记录,filtered就能提升到100。
Extra
额外信息
- Using filesort: 使用了文件排序
- Using temporary: 使用了临时表,常见于 order by 和 group by
- Using index: 覆盖索引,不用回表
- Using where: 索引有被用于where过滤
总结
本文简单介绍了 EXPLAIN 输出的各字段的含义,EXPLAIN 常被用来分析查询的执行效率。为了优化查询,重点关注type
字段。