通过在要执行的SQL语句前增加explain
这样的命令,Mysql不会执行SQL语句而是返回这个语句的执行计划的一些信息。 该命令在某些时候可以帮助我们快速地得知SQL语句有什么问题,有没有可以优化的地方。 但是请注意该命令返回的信息并不总是说出真相。 一个简单的explain语句可能像下面这样:
EXPLAIN SELECT 1 FROM DUAL;
它返回一行或者多行数据,每一个都是执行计划的一个步骤,而且是按照执行计划执行的顺序排列。 结果如下: {% asset_img 1.png %}
Explain命令有诸多限制,有时候甚至返回的结果与真相相差很远: - 没法告诉用户触发器、存储过程会对查询造成什么影响 - 没法支持存储过程的解释 - 不会告诉你查询执行中做的特定优化 - 对内存排序和临时文件都使用“filesort”来表示,对磁盘和内存中的临时表都使用“Using temporary”来表示,导致用户没法详细地分清这些情况 - 不能解释除了select以外的其他语句,比如update、insert、delete等(5.6版本之后貌似可以了)
就是一个编号,代表子查询在SQL语句中的位置(官方文档也没有详细说明到底代表啥意思,但是感觉像是SELECT关键字出现在SQL语句中的位置)。 有时候id有可能都是1(关联查询中),有时候还有可能是Null(在使用union语句的时候)。 ### select_type 显示了查询的类型,是一个简单查询还是复杂查询,是哪种复杂查询。 可能出现的值有如下这些(5.6版本): - SIMPLE:简单查询,标识查询不包含任何子查询或者UNION语句 - PRIMARY:复杂查询的外层查询,一般都在第一行,代表这是一个复杂查询的最外层查询 - SUBQUERY:复杂查询的子查询,指不在FROM子句中的那些 - DEPENDENT SUBQUERY:复杂查询中,依赖外部查询的子查询 - DERIVED:在FROM子句中的子查询 - UNION:复杂查询中,UNION子句第二个或之后的子查询(第一个被标为PRIMARY) - DEPENDENT UNION:复杂查询中,依赖外部查询的UNION子句查询 - UNION RESULT:复杂查询中,UNION的结果,这是一个从匿名临时表检索最终结果的查询 - MATERIALIZED:复杂查询中,物化视图子句 - UNCACHEABLE SUBQUERY:不可缓存的子查询 - UNCACHEABLE UNION:不可缓存的UNION子查询
表示对应行的语句访问哪个表。 如果是关联查询,你会发现id都是1,但是从上往下的顺序还是代表了关联语句的执行顺序,有时你会看到explain的结果中的顺序与你想象的不一样,不用怀疑,语句的确是按照结果中的顺序执行的,引擎对你的语句进行了优化,因此执行顺序有了变更。 在FROM子句中有子查询时,该子查询的table字段一般的都是<derivedX>
其中X指的是id,一般都是其后面一行的子查询的id。 另外UNION RESULT行的table会显示成<unionX,Y……>
,其中X,Y等指的是参与UNION的子句。 ### type 这一列非常重要,表示了Mysql究竟采取何种方法来访问数据,通常能够在一定程度上反映查询语句的性能。 可能出现的值有如下这些,性能从最差到最优(5.6版本): - ALL:全表扫描,最惨的性能,从数据表中逐行查找数据。除非使用了LIMIT或者在Extra列中有“Using distinct/not exists”字样 - index:全表扫描的进阶版,按索引顺序全表扫描,通常性能和全表扫描没什么区别,除非Extra列中有“Using index”字样,那说明使用了覆盖索引,这种情况下要快于ALL,因为直接扫描索引就能获取数据,而索引通常比表小的多。如果所要查询的列是某个索引的一部分,通常会出现这种查询。 - range:范围扫描,比index强一些,因为它是从索引的某个点开始的,用不着遍历全部索引。一些带有BETWEEN,各种比较符号的语句容易出现这种类型,但是要特别注意IN和OR,这也会显示成range,但是其性能跟index差不多。 - index_subquery:索引替换子查询,如果有这样的语句SELECT * FROM table WHERE value IN(SELECT key_column FROM table where xxx)
,IN后面的语句会被索引直接代替,来提高效率。 - unique_subquery:唯一索引替换子查询,和上面的相同,只不过是使用唯一索引来替换IN后面的子句 - index_merge:以前Mysql一次查询只能使用一个索引来优化,好像从5.5版本开始,Mysql支持多个索引同时优化,本质上是每个索引单独使用,再通过某种算法来合并结果。 - ref_or_null:类似于下面要介绍的ref类型,但是Mysql做了额外的搜索,为了找到那些包含NULL值的行,通常这样的语句比较容易出现这个类型:SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL
。 - fulltext:全文索引使用时会出现此类型。 - ref:当使用除了主键索引、唯一索引以外的索引来匹配值时,或者使用了最左前缀索引(包括唯一索引和主键的),就会出现这个类型。例子如下: SELECT * FROM ref_table WHERE key_column=expr
SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.key_column
SELECT * FROM ref_table,other_table WHERE ref-table.key_column_part1=other_table.column AND ref_table.key_column_part2=1
- eq_ref:当使用了主键索引,唯一索引等只会唯一地找出一条记录的情况,这种类型就会出现,这种类型的性能相当好。 - const:当查询唯一地匹配一条记录,并且使用主键等于常数或者唯一索引等于某常数作为查询条件时,Mysql会视查询出来的值为常数,这种类型非常快。比如:SELECT * FROM tbl_name WHERE primary_key = 1;
- system:表只有一行记录且为系统表,Mysql官方没有给出出现这个类型的例子
查询可能会用到的索引
显示查询最终使用到的索引,如果该索引没有出现在possible_key里,那么它可能是一个覆盖索引。 如果显示的是NULL,很遗憾没有任何索引被用到,说明查询的性能可能会很差。
显示查询时使用到的索引的字节数,根据这个值可以判断出来究竟使用了索引里的哪些列。 可以为NULL的列会比不能为NULL的列多一个字节。
显示哪些列或者常量被用来跟key中显示的索引进行比较,从而获取结果。 如果显示func,则说明是某些function的结果被用来进行比较。
显示Mysql引擎认为它要获得结果预计要扫描的行数。这是一个估计值,可能不是很精确。 注意这个值不是结果集的行数,还要知道有很多优化手段没能影响这个值,因此可能最终执行时不必读取这么多行记录。 如果有多行结果,将多行的rows相乘可以得到一条完整语句执行预计要扫描的行数。