前言
调用 EXPLAIN 来获取关于查询执行计划的信息与输出
调用 EXPLAIN
-
使用:在 SELECT 关键字之前增加 EXPLAIN
-
每个表在输出中只有一行,若多表关联,则输出多行
- 这里的表的定义:可以是一个子查询,一个 UNION 结果
- EXPLAIN 的两个主要变种
- EXPLAIN EXTENDED
- 服务器 “逆向编译” 执行计划为一个 SELECT 语句(SHOW WARNINGS 后能看到
- 额外增加一个 filtered 列
- EXPLAIN PARTITIONS
- 显示查询将访问的分区
- EXPLAIN EXTENDED
-
EXPLAIN 查询时,会执行子查询并将其结果放在一个临时表中,然后完成外层查询优化
-
EXPLAIN 返回的只是个近似结果
-
EXPLAIN 相关的限制
-
无法知道触发器、存储过程或 UDF 如何影响查询
-
不支持存储过程(单可以单独抽取查询进行 EXPLAIN
-
无法知道查询执行中所做的特定优化
-
不会显示关于查询的执行计划的所有信息
-
无法区分具有相同名字的事物
- 内存排序 和 临时文件 都使用 filesort
- 磁盘上和内存中的临时表都显示 Using temporary
-
可能会误导(对一个有着小 LIMIT 的查询显示 全索引扫描
重写非 SELECT 查询
-
EXPLAIN 只能解释 SELECGT 查询,无法对 INSERT、UPDATE、DELETE 或其他语句做解释
-
可通过重写某些非 SELECT 查询以利用 EXPLAIN
- 转化成一个等价的访问所有相同列的 SELECT
- 任何提及的列都必须在 SELECT 列表,关联子句 或者 WHERE 子句中
-
显示计划时,对于写查询并没有“等价”的读查询
- 读只需要找到数据的一份副本并返回
- 写需要查找并修改其所有副本(消耗更高
EXPLAIN 中的列
-
id 列
-
总是包含一个编码,标识 SELECT 所属的行
-
简单查询类型
-
复杂查询类型
- 简单子查询
- 派生表
- UNION
-
UNION 结果总是放在一个匿名临时表中(它的 id 列是 NULL
-
-
select_type 列
- SIMPLE
- 不包括子查询和 UNION
- PRIMARY
- 查找中存在复杂的子部分,最外层部分标记为 PRIMARY
- SUBQUERY
- SELECT 列表中的子查询中的 SELECT
- DERIVED
- FROM 子句的子查询中的 SELECT(派生表
- UNION
- 在 UNION 中的第二个和随后的 SELECT 被标记为 UNION
- UNION RESULT
- 从 UNION 的匿名临时表检索结果的 SELECT
- DEPENDENT
- SELECT 依赖与外层查询中发现的数据
- UNCACHEABLE
- SELECT 中的某些特性阻止结果被缓存于一个 Item_cache 中
- SIMPLE
-
table 列
- 显示对应行正在访问哪个表
- MySQL 的查询执行计划总是左侧深度优先树
-
type 列
- 显示了 关联类型(访问类型
- 常用的访问类型(性能依次从最差到最优
- ALL
- 全表扫描(存在例外,例如使用了 LIMIT 或 Extra 列中显示 Using distinct/not exists
- index
- 类似于全表扫描,只是扫描时按索引次序进行
- 优点:可以避免排序
- 缺点:承担按索引次序读取整个表的开销(意味着按随机次序访问行,开销较大
- range
- 有限制的索引扫描(范围扫描
- IN ( ) 和 OR 列表使用时均显示为 范围扫描,但两者其实是相当不同的访问类型,性能差异较大
- ref
- 索引访问(索引查找),返回所有匹配某个单个值的行
- 使用场景:使用非唯一索引时 或 唯一索引的非唯一性前缀时
- ref_or_null(变体):MySQL 必须在初次查找的结果里进行第二次查找以找出 NULL 条目
- eq_ref
- 最多只返回一条符合条件的记录
- 使用场景:使用主键 或 唯一索引时
- const ,system
- MySQL 能对查询的某部分进行优化并将其转换成一个常量时
- NULL
- MySQL 能再优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引
- ALL
-
possible_keys 列
- 显示查询可能使用哪些索引
-
key 列
- 显示 MySQL 决定采用哪个索引来优化对该表的访问(基于最小化查询成本
- 该索引可能不存在 possible_keys 列中(可能选择了一个覆盖索引,哪怕没有 WHERE 子句
-
key_len 列
- 显示 MySQL 在索引里使用的字节数(具体使用到的该索引上的某些列
- key_len 通过查找表的定义而被计算出,而不是表中的数据
-
ref 列
- 显示在 key 列记录的索引中查找值所用的列或常量
-
rows 列
- 显示 MySQL 估计为了找到所需要的行而要读取的行数(嵌套循环管理计划里的嵌套数目
- 该数字只是 MySQL 认为它要检查的行数,而不是结果集里的行数
-
filtered 列
- 使用 EXPLAIN EXTEND 时才会出现
- 显示 针对表里符合某个条件的记录数的百分比所做的一个悲观估算
- 只有在使用 ALL、index、range 和 index_merge 访问时才会用到这一估算
- Extra 列
- 显示不适合在其他列显示的额外信息
- 常见重要的值
- Using index(覆盖索引
- Using index condition(ICP 索引条件下推
- Using where(Mysql 服务器将在存储引擎检索后再进行过滤
- Using temporary(对查询结果排序时会使用一个临时表
- Using filesort(对结果使用一个外部索引排序,详情请看:《高性能 MySQL》查询性能优化
- Range checked for each record(没有好用的索引,新索引将在联接的每一行上重新估算
MySQL 5.6 中的改进
- 能对类似于 UPDATE、INSERT等的查询进行解释
- 对查询优化和执行引擎的一系列改进
- 允许匿名的临时表尽可能晚地被具体化
- 可直接解释带子查询的查询语句,而不需要先实际地执行子查询
- 增加优化跟踪功能的方式改进优化器