type列
MySQL手册上注明type列用于描述join type,不过我们认为把这一列视为对access type--即MySQL决定如何在表中寻找数据的方式的描述,更加合适一些,以下所示从最坏情况到最好情况下的access类型:
ALL
All类型即常说的全表扫描,通常意味着MySQL需要从头到尾扫描整张表以找到符合条件的行记录(使用LIMIT限定的查询语句个例外,一般在extra列中会有"Using distinct/not exists"的补充描述)。
index
基本和全表扫描一样,但是是通过索引的顺序扫描,主要优化是避免了排序,最大的缺点是通过索引顺序扫表时,通常意味着从存储空间中乱序取数据,IO花销会非常大。
如果在Extra列中还有"Using index"的说明,意味着MySQL使用一个覆盖索引实现扫表,而不是使用索引顺序扫表,这会比通过索引顺序扫表具有更好的性能。
range
一个范围遍历是一个限制版本的索引遍历,从索引中的某个位置开始,返回一定范围内的所有行记录,这会比扫描整个索引更快,这类查询的where子句中一般伴有BETWEEN或者>这类关键词。
当MySQL使用索引查找IN或者OR包含的一系列值时,也会显示为一个范围遍历,但是这两种其实是非常不同的访问类型,两者之间有着重要的性能差异。
ref
这是一个返回匹配特定单一值行记录的索引访问(索引查找)类型,实际上由于可能存在多行匹配的情况,这是一个查找和遍历的混合类型。这种索引查找仅能用于非唯一索引或者一个唯一索引的非唯一前缀索引。之所以叫做ref类型是因为索引被用于和一些参考值(reference value)进行比较,参考值只能一个常量或者多表查询中之前一个表中的某个值。
ref_or_null是ref的一个变种,意思是MySQL在做了第一次查找后,还必须做第二次查找查找为NULL的记录。
eq_ref
这是MySQL知道最多只会返回一个匹配结果的索引查找类型。当针对主键或者唯一索引和一些引用值比较时,就是这种访问类型,MySQL能针对这种类型做非常好的优化,因为不需要预估匹配行记录的范围或者在找到一行记录后再继续查找。
const,system
当MySQL能够将查询中的某些部分优化为一个常量时,就是这中访问类型。如果你将主键放置在where子句中,同时在select部分选取该主键,MySQL就会将其转化为一个常量,然后从join执行中高效的移除这个表。
NULL
NULL访问类型标识MySQL在优化阶段就能解析查询,甚至不需要在执行阶段实际访问到表数据或者索引,例如从索引列中获取最小值,只需要查看一下索引,不要求执行期间访问任何表数据。
possible_keys列
这一列基于查询语句中访问到和用于比较操作的列名,展示那些可能用于查询的索引名称列表,这个列表在优化阶段的早期生成,所以在经过后面的优化阶段后,其中的一些索引可能就并不需要用到了。
key列
这一列展示MySQL实际使用哪个索引优化对于表的访问,如果这个索引没有出现在possible_keys中,表明MySQL是基于其他特殊理由选定的该索引--比如即便查询没有where子句,MySQL也可能会优先选择使用覆盖索引。
换句话所,possible_keys揭示了哪些索引能够帮助行记录查询更加高效,而key则显示了优化器实际决定使用来最小化查询花销,下面是一个实际例子。
key_len列
这一列展示MySQL使用的索引的长度(字节个数),如果MySQL只使用索引中的一部分列,可以通过这个值计算出MySQL实际使用的是哪些列。注意MySQL5.5及以下版本只能使用最左前缀匹配。下面这个例子中sakila.film_actor的主键包括两个SMALLINT列,一个SMALLINT是两个字节,所以主键索引长度应该是4字节:
根据key_len,我们能推断出上面的查询语句仅在第一列(actor_id)上使用了索引查找。当计算列使用情况时,还需要考虑到字符类型列字段实际使用的字符集:
上面查询中a列和b列组成的索引长度为13字节,a列只有三个字符大小,但是utf8字符集使用最多3个字节标识一个字符,b列则是4字节整数。
MySQL并不总是会告诉你一个索引被多大程度使用到,如果你使用一个前缀匹配的LIKE查询,它将显示被使用那一列的总长度。
key_len列标识索引字段可能被使用到的最大长度,而不是表中数据使用的实际字节数量,即便上面的列a凑巧不包含任何长度大于一字符的值,MySQL依然会显示13字节长度,也就是说,key_len是通过表的定义计算得出,而非表中的实际数据。
ref列
这字段标识之前的表中哪些列或者常量被用于key列中的索引进行查找,下面是一个join和alias使用的例子,注意ref列反映了film表在查询语句中怎么被别称为f:
rows列
这一列展示MySQL估计查询语句执行时需要实际读取数据的记录行数,针对一个多重嵌套查询的语句,这个数字并不只是MySQL认为他需要从表中读取的实际行数,它将是每一个子查询阶段中MySQL估计满足当前查询条件所需要读取的行数。
根据表的统计信息已经索引的选择情况,这个估计可能很不准,MySQL5.0及以前,不会考虑LIMIT的影响,如下例:
你可以通过将每一行中这列的值相乘以得到整个查询语句大概要检查的行记录数量,如下例大概会检查2600行记录:
注意这个数量是MySQL估计会检查的行数,并不是结果集中的行数,并且诸如连接缓存这些优化,并不会影响到这一行的数值,即便MySQL可能并不会实际检查它估计的每一行数据,MySQL也不知道操作系统或者硬件缓存所做的任何优化。
filtered列
当使用EXPLAIN EXTENDED时,MySQL5.1及以上版本会有这一列,它针对满足where语句或者join条件的行数百分比做一个悲观估计,如果将rows列的数值乘以这个百分比,可以得到MySQL估计其会与前面的表数据进行join的行数量,优化器使用这一估计应用于ALL/index/range和index_merge访问类型。
假设下面一个标结构:
往表中插入1000条filler为随机的文本数据,以避免MySQL会针对我们的查询使用覆盖索引:
MySQL本可以使用一个range access通过ID从表中检查ID<500的行记录,但是考虑到这只能优化大概一半的行访问,它认为一个全表扫表的花销反而更少些,于是MySQL选择全表扫表时通过WHERE子句中的条件过滤行记录。通过range access的估计,MySQL能知道大概有多少行会被WHERE条件过滤,于是filtered列中的值就是49.40%了。
Extra列
这一列展示了其他列所没有的额外信息,MySQL文档上说明了能够出现在这一列的大多数值,你能够经常见到的最重要的一些取值如下:
Using index
MySQL会使用覆盖索引避免直接访问表数据,不要把覆盖索引和index访问类型弄混。
Using where
MySQL把数据从存储引擎中检索出来后,才会使用where条件进行过滤,WHERE子句中涉及到索引列的条件很多都可以在读取索引时被检测,所以并不是所有有WHERE子句的查询都会有“Using where”这一信息,有时候这个信息的存在暗示查询可能能够通过不同的索引策略获得优化。
Using temporary
MySQL会使用改一个临时表存储查询结果。
Using filesort
MySQL会对使用外部排序对结果排序,而不是通过索引顺序读取表数据,MySQL有两种filesort算法,都可以在内存或者硬盘上进行排序,EXPLAIN并不会指出具体使用的是哪种filesort算法,也不会指出是在内存还是硬盘上进行排序。
Range checked for each record(index map:N)
这意味着没有好的索引策略,join子句中的每一行都要重新计算索引,N表示possible_keys中的一个冗余的索引bitmap。