导读:
当一个sql查询语句被写出来之后,其实你的工作只完成了一小半,接下来更重要的工作是评估你自己写的sql的质量与效率。mysql为我们提供了很有用的辅助武器explain,它向我们展示了mysql接收到一条sql语句的执行计划。根据explain返回的结果我们便可以知道我们的sql写的怎么样,是否会造成查询瓶颈,同时根据结果不断的修改调整查询语句,从而完成sql优化的过程。
虽然 explain返回的结果项很多,这里我们只关注如下几点:
准备三张表:

1)ID值:
1.1、ID值相同,从上至下依次顺序执行,且表的数据量越小越被先执;
mysql> explain select t.* from teacher t,course c,teacherCard tc
-> where t.tid=c.tid and t.tcid=tc.tcid and (c.cid=3 or tc.tcid=3);

1.2、ID值不同,越大越先执行(先执行最内层子查询,再查子查询,再查主查询);
mysql> explain select tc.tcdesc from teacherCard tc where tcid=(
-> select t.tcid from teacher t where t.tid=(
-> select c.tid from course c where c.cname='sql'));

1.3、ID相同又不同,先执行ID大的,后依次顺序执行ID相同的;
mysql> explain select t.tname,tc.tcdesc from teacher t,teacherCard tc
-> where t.tcid=tc.tcid and t.tid=(
-> select c.tid from course c where cname='sql');

2)select type
PRIMARY:SQL中包含子查询种的主查询(最外层查询);
SUBQUERY:被包含在主查询(外层查询)中的查询(非最外层查询);
simple:检查查询(不含子查询、union);
derived:衍生查询(查询中用到了临时表);
a.在from子查询中只有一张表
explain select cr.cname from (select * from course where tid in (1,2))cr;

b.在from子查询中,如果有table1 union table2,则table1就是derived,table2是union表
explain select cr.cname from (select * from course c1 where tid=1 union select * from course c2 where tid=2) cr;

union result:告知开发人员,那几行表之间进行了union操作。
3)type:索引类型
常见的type类型:system>const>eq_ref>ref>range>index>all,type优化前提是有索引。
其中:实际能达到的只有ref和range级别。
3.1、const:仅仅能查到一条数据的SQL,且用于Primary key或unique索引(主要与索引类型有关);
准备一张只有一条数据的表test01

为test01表添加unique索引

mysql> explain select tid from test01 where tid=1;

将unique index变为普通index后结果如下:

3.2、eq_ref:唯一索引,对于每个索引键的查询,返回匹配唯一一行数据(有且只有一行,不能为0行或多行);
mysql> select t.tcid from teacher t,teacherCard tc where t.tcid=tc.tcid;

mysql> explain select t.tcid from teacher t,teacherCard tc where t.tcid=tc.tcid;

以上SQL,用到的索引是t.tcid,即teacher表中的tcid字段;如果teacher表的数据个数和连接查询的数据个数一致,则有可能达到eq_ref级别。
3.3、ref:非唯一性索引,对于每个索引键的查询,返回匹配的所有行数据(0或多行)。
先看teacher表数据

给teache在tname字段上加索引

mysql> explain select * from teacher where tname='tz';
mysql> explain select tname from teacher where tname='tz';

3.4、range:检索指定范围的行,where后面是一个范围查询(between,in,> ,<,>=)

3.5、index:查询全部索引数据
explain select tname from teacher;

3.6、all:查询所有数据

小结:
system/const:结果只有一条数据;
eq_ref:结果多条 ,但是每条数据是唯一的;
ref:结果多条,但是每条数据是0或多条。
4)possible_keys:可能用到的索引,是一种预测,不一定是实际用到
5)key:实际用到的索引,NUll表示没用到索引
6)key_len:索引的长度
作用:用于判断复合索引是否被完全使用。
准备一张表test_k1表

为name添加索引


给name1也增加索引


说明:utf8中,如果字段值为空,则mysql用1个字节标识控制,key_len=20*3+1=61
删除之前的索引,创建复合索引

mysql> explain select * from test_k1 where name1='';

为name2列加索引,name2类型为varchar(20)
mysql> alter table test_k1 add index idx_name2(name2);


说明:在utf8中,mysql用两个字节标识可变长度varchar,1个字节标识null,key_len=20*3+1=2=63。
7)rows:被索引优化查询的数据个数
8)Extra:
8.1、using filesort:性能消耗大,需额外的意思排序(查询),常见于order by中。
准备表:

8.1.1、单列索引:给a1列添加单列索引
mysql> alter table test02 add index idx_a1(a1);

mysql> explain select a1 from test02 order by a1;

小结:对于单索引如果排序和查找是同一个字段,则不会出现using filesort。
8.1.2、复合索引:不能跨列,最佳左前缀
创建复合索引:
mysql> alter table test02 add index a1_a2_a3(a1,a2,a3);

mysql> explain select * from test02 where a1='' order by a3;



小结:where和order by按照复合索引的顺序使用,不要跨列使用。
8.2、using temporary:性能损耗大,用到了临时表,常见于group by中。
mysql> explain select a1,a2 from test02 where a1=1 and a2=2 group by a3;

mysql> explain select a1,a2 from test02 where a1=1 and a2=2 group by a2;

mysql> explain select a1,a2 from test02 where a1=1 and a2=2 group by a1;

mysql> explain select a1,a2 from test02 where a1=1 and a2=2 group by a1,a2;

mysql> explain select a1,a2 from test02 where a1=1 and a2=2 group by a2,a1;

mysql> explain select a1,a2 from test02 where a2=2 and a1=1 group by a2,a1;

小结:查询顺序、筛选顺序和分组顺序一定要一直才不会出现using temporary。
8.3、using index:性能提升,索引覆盖
原因:只从索引中读取数据,不需要回表查询。只要使用到的列全部包含在索引里,那么就是using index索引覆盖。

mysql> explain select a1,a2,a3 from test02;

小结:
using index时,会对possible_keys和key造成影响
a.如果没有where,则索引只出现在key中;
b.如果有where,则索引出现在key和possibles_key中。
8.4、using where:必须要回表查。
删除基于a1、a2、a3的复合索引,创建基于a1,a2的复合索引


8.5、immpossible where:where子句永远为false。
mysql> explain select * from test02 where a1='x' and a1='y';
