1. EXPLAIN介绍
⏰1.1 EXPLAIN执行计划介绍
- 要使用
EXPLAIN
,只需要在查询中 SELECT 关键字之前增加EXPLAIN
这个词。MySQL 会在查询上设置一个标记。当执行查询时,这个标记会使其返回关于在执行计划中每一步的信息,而不是执行它它会返回一行或多行信息,显示出执行计划中的每一部分和执行的次序。 - 需要人认为添加了
EXPLAIN
的 SQL 语句不会执行查询,这是一个常见的错误。事实上,如果查询在FROM
子句中包含子查询,那么MySQL
实际会执行子查询,将其结果放在一个临时表中,然后完成外层查询优化。它必须在可以完成外层查询优化之前处理所有类似的子查询,这对于EXPLAIN
来说是必须要做的(在5.6版本中取消此限制)。这意味者如果语句包含开销较大的子查询或使用临时表算法的视图,实际上会给服务器带来大量工作。
⏰1.2 EXPLAIN简单范例
- 如下展示了一个最简单的 EXPLAIN 结果:
🍈
mysql> EXPLAIN SELECT 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set (0.10 sec)
- 上述范例只输出了一行,但是在查询语句中每个表都会在输出结果中占一行。如果查询是两个表的联结,那么输出中将有两行。
- ① 别名表单算为一个表,因此,如果一个表与自己联接,输出中也会有两行。②
表是一个广泛的定义
:可以是一个子查询,也可以是一个 UNION 结果。
⏰1.3 EXPLAIN的变种
EXPLAIN EXTENDED
:看起来和正常的 EXPLAIN 的行为一样,但它会告诉服务器 “逆向编译” 执行计划为一个 SELECT 语句。可以通过紧接其后运行 SHOW WARNINGS 看到这个生成的语句。这个语句直接来自执行计划,而不是原SQL语句,到这点上已经变成了一个数据结构。在大部分场景下它都与愿意语句不同。我们可以检测查询优化器到底如何转化语句的。EXPLAIN PARTITIONS
:会显示查询将访问的分区,如何查询是基于分区表的话。
⏰1.4 EXPLAIN的相关限制
- 需要说明的是
EXPLAIN
只是一个近似的结果。有时候它是一个很好的近似,但是在有些情况下,可能与真实情况相差甚远。如下是一些相关限制:😳
EXPLAIN
根本不会告诉我们触发器,存储过程或者 UDF 如果影响查询。- 它并不支持存储过程,尽管可以手动抽取查询并单独地对其进行
EXPLAIN
操作。 - 它并不会告诉我们 MySQL 在查询执行中所做的特定优化。
- 它并不会显示关于查询的执行计划的所有消息。
- 它并不区分具有相同名字的事务,例如,它对内存排序和临时文件都使用“filesort”,并且对于磁盘上和内存中的临时表都显示“Using temporary”。
- 可能会误导。例如,它会对一个有着很小的 LIMIT 的查询显示全索引扫描。(MySQL5.1版本后不会有此限制)。
2. 重写非SELECT查询
⏰ MySQL5.6 中可以对UPDATE,INSERT 等语句进行解释,所以在此版本中没有必要重写非SELECT查询。
3. EXPLAIN中的id列
⏰3.1 id列介绍以及分类
id列介绍
:这一列总是包含一个编号,标识 SELECT 所属的行(💯标识所属哪个主查询或者哪个子查询)。如果在语句当中没有子查询或者联合查询,那么只会有唯一的SELECT
,于是在这个列中都将显示一个1。否则,💯内层的 SELECT 的语句一般会有顺序编号,对应于其在原始语句中的位置。id列分类
:MySQL 将 SELECT 查询分为简单和复杂类型,复杂类型可分为三大类:① 简单子查询;② 派生表(在 FROM 子句中的子查询)③ UNION 查询。
⏰3.2 id列分类: 简单子查询
- 下面是一个简单的子查询:
🍈
mysql> EXPLAIN SELECT ( SELECT 1 FROM sakila.actor LIMIT 1 ) FROM sakila.film;
+----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
| 1 | PRIMARY | film | NULL | index | NULL | idx_fk_language_id | 1 | NULL | 1000 | 100.00 | Using index |
| 2 | SUBQUERY | actor | NULL | index | NULL | idx_actor_last_name | 182 | NULL | 200 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
可见主查询是 id 值是1 ,子查询 id 值是2。
⏰3.2 id列分类: 派生表( FROM 后的子查询)
FROM
子句中的子查询和联合给 id 列增加了更多复杂性。下面是一个 FROM 子句的范例:
🍈
mysql> EXPLAIN SELECT * FROM ( SELECT 2 ) AS der ;
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+----------------+
| 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+----------------+
这个查询执行时有一个匿名临时表。MySQL内部通过别名 ( der ) 在外层查询中引用这个临时表,在更复杂的查询中可以看到 ref 列。
⏰3.3 id列分类: UNION 查询
UNION
和UNION ALL
都有此特性,如下是一个UNION
查询:
🍈
mysql> EXPLAIN SELECT 1 UNION SELECT 1 ;
+------+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
| 2 | UNION | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+------+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
注意
UNION
结果输出的Extra
。UNION 结果总是放在一个匿名的临时表中,之后 MySQL 将结果读取到临时表外。临时表并不在原 SQL 中出现,因此它的 id 列是NULL
。与 FROM 子句相比,这个查询产生的临时表在结果中出现在最后一行,而不是第一行。
4. EXPLAIN中的select_type列
⏰4.1 SIMPLE
- 表示 SELECT 查询语句是💯简单查询,意味着查询不包含子查询和 UNION。若类型非 SIMPLE ,则为复杂查询(三种复杂类型中的一种或者三种类型的复合体)。
- 例如:🍈EXPLAIN SELECT 1
⏰4.2 PRIMARY
- 如果查询有复杂的子部分,则最外层部分标记为
PRIMARY
类型。 - 例如:🍈EXPLAIN SELECT ( SELECT title FROM film ) FROM film ;
⏰4.3 SUBQUERY
- 包含在 SELECT 列表中的子查询中的 SELECT (换句话说,不在 FROM 子句中)标记为
SUBQUERY
类型。 - 例如:🍈EXPLAIN SELECT ( SELECT title FROM film ) FROM film ;
⏰4.4 DERIVED
- DERIVED 值用来表示包含在 FROM 子句的子查询中的 SELECT,MySQL 会递归执行并将结果放到一个临时表中。服务器颞部称其“派生表”,因为该临时表从子查询中派生而来的。
- 例如:🍈EXPLAIN SELECT * FROM ( SELECT 2 ) AS der ;
⏰4.5 UNION
- 在 UNION 中的第二个和随后的 SELECT 被标记为 UNION。
- 第一个 SELECT 正常标记。这就是在之前的例子 UNION 中的第一个 SELECT 显示为 PRIMARY 的原因。如果 UNION 被 FROM 子句中的子查询包含,那么它第一个 SELECT 会被标记为 DERIVED。
- 例如:🍈EXPLAIN SELECT 2 UNION SELECT 1;
⏰4.6 UNION RESULT
- 用来从 UNION 的匿名临时表检索结果的 SELECT 被标记为 UNION RESULT。
- 例如:🍈EXPLAIN SELECT 2 UNION SELECT 1;
⏰4.7 DEPENDENT和UNCACHEABLE
- 除了上述值,SUBQUERY 和 UNION 还可以标记为 DEPENDENT 和 UNCACHEABLE。
DEPENDENT
意味者 SELECT 依赖于外层查询中发现的数据;UNCACHEABLE
意味着 SELECT 中的某些特性阻止结果被缓存于一个 Item_cache中。
5. EXPLAIN中的table列
⏰5.1 table列
- 这一列显示了对应行正在访问哪个表。在通常情况下,它相当明了;它就是那个表,或者是该表的别名(若 SQL 中定义了别名)
- 此列由上到下可以查看到 MySQL 的关联优化器为查询选择的关联顺序。
- 范例
🍈
mysql> EXPLAIN SELECT film.film_id FROM sakila.film INNER JOIN sakila.film_actor USING(film_id) INNER JOIN sakila.actor USING(actor_id);
+----+-------------+------------+
| id | select_type | table |...
+----+-------------+------------+
| 1 | SIMPLE | actor |...
| 1 | SIMPLE | film_actor |...
| 1 | SIMPLE | film |...
+----+-------------+------------+
范例中,可以看到 MySQL 在查询中选择的关联顺序不同于语句中所指定的顺序,是因为 MySQL 的查询执行计划总是左侧深度优先树。
⏰5.2 派生表和联合
- 当 FROM 子句中有子查询或者有 UNION 时,table 列会变得复杂很多。在这些场景下,确实没有一个“表”可以参考到,因为 MySQL 创建的匿名临时表仅在查询执行过程中存在。
(派生表)FROM
子句中的子查询,table 列是 <derivedN> 的形式,其中 N 是子查询的 id。这总是 “向前引用” – 换而言之, N 指向 EXPLAIN 输出中后面的一行。(联合)UNION
查询,当有 UNION 查询时,UNION RESULT 的 table 列包含所有参与 UNION 的 id 列表。这总是 “向后引用”,因为 UNION RESULT 出现在 UNION 中所有参与行之后。
如果在列表中有超过 20 个 id , table 列可能被截断防止太长,此时不可能看到所有的值。但是我们仍然可以推测包含哪些行。
⏰5.3 复杂 SELECT 例子
- 如下是一个无意义的查询,这里把它用作复杂 SELECT 类型的示例。
🍈
EXPLAIN
SELECT
actor_id,
( SELECT 1 FROM sakila.film_actor WHERE film_actor.actor_id = der_1.actor_id LIMIT 1 )
FROM
( SELECT actor_id FROM sakila.actor LIMIT 5 ) AS der_1
UNION ALL
SELECT
film_id,
( SELECT @var1 FROM sakila.rental LIMIT 1 )
FROM
( SELECT film_id, ( SELECT 1 FROM sakila.store LIMIT 1 ) FROM sakila.film LIMIT 5 ) AS der_2;
+----+----------------------+------------+
| id | select_type | table |
+----+----------------------+------------+
| 1 | PRIMARY | <derived3> |
| 3 | DERIVED | actor |
| 2 | DEPENDENT SUBQUERY | film_actor |
| 4 | UNION | <derived6> |
| 6 | DERIVED | film |
| 7 | SUBQUERY | store |
| 5 | UNCACHEABLE SUBQUERY | rental |
|NULL| UNION RESULT | <union1,4> |
+----+----------------------+------------+
EXPLAIN
解释上述 SELECT 查询第1行
向前引用了 der_1,这个查询被标记为 。在原 SQL 中是第2行。想了解输出中哪些行引用了 中的 SELECT 语句,向下查看 …- …
第2行
,它的 id 是3。因为它是查询中第3个 SELECT 的一部分,归为 DERIVED 类型是因为它嵌套在 FROM 子句中的子查询内部。在原 SQL 中是第6~7行。 第3行
的 id 为2。在原SQL中为第3行。注意,它在具有更高id的行后面,暗示后面再执行
,这是合理的。它被归为 DEPENDENT SUBQUERY,意味着其结果依赖于外层查询(亦某个相关子查询)。本例外查询是第2行开始,从 der_1 中检索数据的 SELECT。第4行
被归为 UNION,意味着它是 UNION 中的第2个或者之后的 SELECT。它的表为 ,意味着从子句 FROM 的子查询中检索数据并附加到 UNION 的临时表。像之前一样,若要找到显示这个子查询的查询计划的 EXPLAIN 行,必须往下看。第5行
是在原 SQL 中第13,14,15行定义的 der_2子查询,EXPLAIN 称其为 。第6行
是 的 SELECT 列表中的一个普通子查询,它的 id 为 7,这是非常重要 …- …
第7行
,id是5,因为它比7小。第6行的id为7,其为什么重要?因为它显示了子查询边界。当 EXPLAIN 输出 SELECT 类型为 DERIVED 的一行时,表示一个“嵌套范围”开始。如果后续行的id更小(本例中5肯定小于6),意味着嵌套范围已经被关闭。这就让我们知道第7行是从中检索数据的 SELECT 列表中的部分。此行查询因为有用户变量,它列的查询类型为 UNCACHEABLE SUBQUERY。 第8行
,最后一行是 UNION RESULT。它代表从 UNION 的临时表中读取行的阶段。我们可以从这行开始反过来向后,如果我们愿意。它会返回 id 是 1和4 的行结果,它们分别引用了和。
6. EXPLAIN中的type列
⭕🌏 访问类型 — 换而言之就是 MySQL 决定如何查找表中的行。下面是最重要的访问方法,依次从最差到最优。
⏰6.1 ALL
ALL
代表全表扫描,通常意味着 MySQL 必须 扫描整张表,从头到尾,去找到需要的行。(例外情况:在查询中使用了 LIMIT,或者 Extra 列中显示“Using distinct/not exists”)
⏰6.2 index
index
搜索方式同全表扫描一样,只是 MySQL 扫描表时 按索引次序进行 而不是行。它的主要优点是避免了排序,最大的缺点是要承担按索引次序读取整个表的开销。这通常意味着若是按随机次序访问行,开销非常大。- 如果 Extra 列中看到“Using index” ,说明 MySQL 正在使用 覆盖索引 ,它只扫描索引的数据,而不是按索引次序扫描全表。它比按照索引次序全表扫描的开销要少很多。
⏰6.3 range
range
范围扫描就是一个 有限制的索引扫描 ,它开始于索引里的某一点,返回匹配这个值域的行。这比全索引扫描好一些,因为它用不着遍历全部索引。显而易见的范围扫描是带有BETWEEN
或在 WHERE 子句里带有>
的查询- 当 MySQL 使用索引去查找一系列值时,例如
IN()
和OR
列表,也会显示为范围扫描。然而,这两者其实是相当不同的访问段类型,在性能上有重要的差异。 - 此类型扫描的开销和索引类型相当。
⏰6.4 ref
ref
这是一种 索引访问 (有时也叫做索引查找),它返回所有匹配某个单个值的行。然而,它可能会找到多个符合条件的行,因此,它是查找和扫描的混合体。此类索引访问只有当 使用非唯一性索引或者唯一性索引的非唯一性前缀 时才会发生。- 把它叫做 ref 是因为索引要跟某个参考值相比较。这个参考值或许是一个常数,或者是来自多表查询前一个表里的结果值。
⏰6.5 ref_or_null
ref_or_null
是 ref 之上的一个变体,它意味着 MySQL 必须在初次查找的结果里进行第二次查找以找出 NULL 条目。
⏰6.6 eq_ref
eq_ref
使用这种索引查找,MySQL 知道最多 只返回一条符合条件的记录 。这种访问方法可以在MySQL使用主键或者唯一性索引查找
时看到,它会将它们与某个参考值做比较。MySQL 对于这类访问类型的优化做的非常好,因为它知道无需估计匹配行的范围或在找到匹配行后再继续查找。
⏰6.7 const,system
- 当 MySQL 能对查询的某部分进行优化并将其 转换成一个常量 时,它就会使用这些访问类型。举例来说,如果你通过将某一行的主键放入 WHERE 子句里的方式来选取此行的主键,MySQL 就能把这个查询转换为一个常量。然后就可以高效地将表从联结执行中移除。
⏰6.8 NULL
- 这种访问方式意味着 MySQL 能在优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引。例如,从一个索引列里选取最小值可以通过单独查找索引来完成,不需要在执行时访问表。
7. EXPLAIN中的possible_keys列
⏰7.1 possible_keys
- 这一列显示了查询可以使用哪些索引,这是基于查询访问的列和使用的比较操作符来判断的。
- 这个列表是在优化过程的早期创建的,因此有些罗列出来的索引可能对于后续优化过程是没有用的。
8. EXPLAIN中的key列
⏰8.1 key
- 这一列显示了 MySQL 决定 采用哪个索引 来优化对该表的访问。如果该索引没有出现在 possible_keys 列中,那么 MySQL 选用其是出于另外的原因 – 例如,MySQL 可能选择了一个覆盖索引,哪怕没有 WHERE 子句。
- possible_keys 揭示了哪一个索引能有助于高效的行查询,而 key 显示的是优化采用哪一个索引可以最小化查询成本。
- 范例:
🍈
mysql> EXPLAIN SELECT actor_id,film_id FROM sakila.film_actor;
+----+-------------+------------+------------+-------+---------------+----------------+ ...
| id | select_type | table | partitions | type | possible_keys | key | ...
+----+-------------+------------+------------+-------+---------------+----------------+ ...
| 1 | SIMPLE | film_actor | NULL | index | NULL | idx_fk_film_id | ...
+----+-------------+------------+------------+-------+---------------+----------------+ ...
9. EXPLAIN中的key_len列
⏰9.1 概述
- 该列显示了 MySQL 在 索引里使用的字节数 。
- 如果 MySQL 正在使用的是索引中的某些列,那么就可以用这个值来算出具体是哪些列。
⏰9.2 范例
- 范例表
sakila.film_actor
的主键是联合主键PRIMARY KEY (actor_id,film_id)
,每个 SMALLINT 列是两字节,则主键索引共四字节,sql 如下:
🍈
mysql> EXPLAIN SELECT actor_id,film_id FROM sakila.film_actor WHERE actor_id=4;
... +------+---------------+---------+---------+ ...
... | type | possible_keys | key | key_len | ...
... +------+---------------+---------+---------+ ...
... | ref | PRIMARY | PRIMARY | 2 | ...
... +------+---------------+---------+---------+ ...
基于结果中的 key_len 列,我们可以推断出查询使用唯一的首列 – actor_id 列,来执行索引查找。
⏰9.3 key_len列字符集
- 在计算 key_len 的时候,需要把字符列中所用的字符集也考虑进去。
- 范例:
🍈
CREATE TABLE t (
a CHAR ( 3 ) NOT NULL,
b INT ( 11 ) NOT NULL,
c CHAR ( 1 ) NOT NULL,
PRIMARY KEY ( a, b, c )
) ENGINE = MyISAM DEFAULT CHARSET = utf8;
INSERT INTO t( a, b, c) SELECT DISTINCT LEFT(TABLE_SCHEMA,3),ORD(TABLE_NAME),LEFT(COLUMN_NAME,1) FROM information_schema.COLUMNS;
EXPLAIN SELECT a FROM t WHERE a='sak' AND b=112;
... +------+---------------+---------+---------+ ...
... | type | possible_keys | key | key_len | ...
... +------+---------------+---------+---------+ ...
... | ref | PRIMARY | PRIMARY | 13 | ...
... +------+---------------+---------+---------+ ...
这个查询的 key_len 长度为
13
字节,即为 a 列和 b 列的总长度。 a 列 3 字符,utf8 编码每一个最多为 3 字节,而 b 列是一个 4 字节整型。
- MySQL 并不总显示一个索引真正使用了多少。例如,对于一个前缀模式匹配执行 LIKE 查询,它会显示列的完全宽度正在被使用。
- key_len 列显示了在索引字段中可能的最大长度,而不是表中数据使用的实际字节数,上述范例中 MySQL 总是显示 13 字节,即使 a 列恰巧只包含一个字符长度。换而言之,
key_len 是通过查找表的定义而别计算出
,而不是表中的数据。
10. EXPLAIN中的ref列
⏰10.1 ref列
- 这一列显示了之前的表 key 列记录的 索引中查找值所用的列或常量 。
- 范例:如下展示关联条件和别名组合的例子
🍈
EXPLAIN SELECT STRAIGHT_JOIN
f.film_id
FROM
sakila.film AS f
INNER JOIN sakila.film_actor AS fa ON f.film_id = fa.film_id
AND fa.actor_id = 1
INNER JOIN sakila.actor AS a USING ( actor_id );
+-------+ ... +--------------------+---------+------------------------+ ...
| table | ... | key | key_len | ref | ...
+-------+ ... +--------------------+---------+------------------------+ ...
| f | ... | idx_fk_language_id | 1 | NULL | ...
| fa | ... | PRIMARY | 4 | const,sakila.f.film_id | ...
| a | ... | PRIMARY | 2 | const | ...
+-------+ ... +--------------------+---------+------------------------+ ...
ref 列也反映了在查询中 film 表是如何以 f 为别名进行查询的。
11. EXPLAIN中的rows列
⏰11.1 rows列
- 这一列是 MySQL 估计为了找到所需要的行而读取的行数。
- 此列是根据表的统计信息和索引的选用情况,来估算需要扫描的行数,这个可能不是很准确:
这个数字是内嵌循环关联计划里的循环数目。也就是说它不是 MySQL 认为它最终要从表里读取出来的行数,而是 MySQL 为了找到符合查询的查询的每一点上标准的那些行而必须读取的行的平均数。(这个标准包括 SQL 里给定的条件,以及来自联结次序上前一个表的当前列。)
- 范例1,在 MySQL5.0 及更早的版本里,它反映不出 LIMIT 子句:
🍈
EXPLAIN SELECT * FROM sakila.film LIMIT 1;
... rows:1022 ...
上述这个查询不会真的检查 1022 行。
- 范例2,通过把所有 rows 列值相乘,可以粗略的估算出整个查询会检查的行数。如下这个查询大约会检查2600行。
🍈
EXPLAIN
SELECT
f.film_id
FROM
sakila.film AS f
INNER JOIN sakila.film_actor AS fa USING ( film_id )
INNER JOIN sakila.actor AS a USING ( actor_id );
... +------+ ...
... | rows | ...
... +------+ ...
... | 200 | ...
... | 13 | ...
... | 1 | ...
这些数字是 MySQL 认为它要检查的行数,而不是结果集里的行数。同时也要认识到有很多优化的手段,例如关联缓冲区和缓存,无法影响到行数的显示。MySQL 可能不必真的读所有它估计到的行,它也并不知道任何关于操作系统或硬件缓存的信息。
12. EXPLAIN中的filtered列
⏰12.1 filtered列
- 这一列是在 MySQL 5.1 里面新加入的,在使用 EXPLAIN EXTENDED 时出现。它显示的是针对表里符合某个条件(WHERE 子句或者联结条件)的记录数的百分比的一个悲观的估算。
13. EXPLAIN中的Extra列
⭕🌏 额外信息 — 这一列包含的是不适合在其他列显示的额外信息。MySQL 用户手册里记录了大多数可以在这里出现的值。常见的最重要的值如下。
⏰13.1 Using index
- 此值表示 MySQL将使用 覆盖索引,以避免访问表。不要把覆盖索引和 index 访问类型弄混了。
⏰13.2 Using where
- 这意味着 MySqL 服务器 将在存储引擎检索行后再进行过滤。许多 WHERE 条件里涉及索引中列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带有 WHERE 子句的查询都会显示 “Using where”。有时 “Using where”的出现就是一个暗示:查询可能受益于不同的索引。
⏰13.3 Using temporary
- 这意味着 MYSQL 在对 查询结果 排序的时候会使用一个 临时表 。
⏰13.4 Using filesort
- 这意味着 MySQL 会对结果使用一个 外部索引排序 ,而不是按索引次序从表里读取行。
⏰13.5 Range checked for each record(index map:N)
- 这个值意味者 没有好用的索引 ,新的索引将在联接的每一行上重新估算。N 是显示在 possible_keys 列中索引的位图,并且是冗余的。