在 SQL 语句前加上 explain 关键词皆可以得到相应的执行计划。其中:在 MySQL8.0 中是支持对 select/delete/inster/replace/update 语句来分析执行计划,而 MySQL5.6 前只支持对 select 语句分析执行计划。

explain 所有字段解析

id 列

id 标识查询执行的顺序:

  1. 当 id 相同时,由上到下分析执行

  2. 当 id 不同时,由大到小分析执行

例子1:

DESC SELECT
	mobile_phone
FROM
	users u
	JOIN `online` o ON u.id = o.user_id

这里发现是 id 是一样的那是从上到下执行,online 表作为驱动表而 user 表作为被驱动表这里预估扫描数据是 1 * 1

例子2:

DESC SELECT
	mobile_phone 
FROM
	users u 
WHERE
	u.id = (
	SELECT
		user_id 
	FROM
		`online` 
WHERE
	login_time < '2022-01-01')

这里发现是 id 是不一样的那是较大的会大的 id 执行,这里这条 SQL 子查询会先执行。

select_type

  1. SIMPLE: 不包含子查询或者 UNION 操作的查询(简单查询)

  2. PRIMARY:查询中如果包含任何子查询,那么最外层的查询则被标记为 PRIMARY

  3. SUBQUERY:select 列表中的子查询 (不依赖子查询)

  4. DEPENDENT SUBQUERY:依赖外部结果的子查询(依赖子查询)

  5. UNION:union 操作的第二个或者之后的查询值为 union

  6. DEPENDENT UNION:当 union 作为子查询时,第二或是第二个后的查询的值为 select_type

  7. UNION RESULT:union 产生的结果集

  8. DERIVED:出现在 from 子句中的子查询(派生表)

table 列

  1. 展示数据库表名(如果表取了别名显示别名)

  2. <unionM, N>:由ID为M、N查询union产生的结果集

  3. /:由ID为N的查询产生的结果(通常也是一个子查询的临时表)

type 列

注意: 在 MySQL 中不一定是使用 JOIN 才算是关联查询,实际上 MySQL 会认为每一个查询都是连接查询,就算是查询一个表,对 MySQL 来说也是关联查询。

这是因为 MySQL 的执行器在处理查询语句时,会对每个表进行扫描,然后将它们之间的关联关系建立起来,这个过程被称为关联优化。在这个过程中,MySQL 会对查询语句进行分析,判断哪些表之间存在关联关系,然后尝试使用关联查询来处理这些关联关系。

即使你只是查询一个单独的表,MySQL 也会将这个表视为一个关联表,因为它在执行查询时也需要扫描该表并将其与其他可能存在的关联表进行比较。

type 的取值是体现了 MySQL 访问数据的一种方式。type 列的值按照性能高到低排列 system >const > eq_ref > ref > ref_or_null > index_merge > range > index > ALL

  1. system: const 连接类型的特例,当查询的表只有一行时使用

  2. const: 表中有且只有一个匹配的行时使用,如队逐渐或唯一索引的查询,这是效率最高的连接方式

  3. eq_ref: 唯一索引或主键查询,对应每个索引建,表中只有一条记录与之匹配【A 表扫描每一行 B 表只有一行匹配满足】

  4. ref_or_null: 类似于 ref 类型的查询,但是附加了对 NULL 值列的查询

  5. index_merge: 表示使用了索引合并优化方法

  6. range: 索引范围扫描,常见于 between、>、<这样的查询条件

  7. index: FULL index Scan 全索引扫描,同 ALL 的区别是,遍历的是索引树

  8. ALL: FULL TABLE Scan 全表扫描,效率最差的连接方式

possible_key、key列

possible_keys 说明表可能用到了哪些索引,而 key 是指实际上使用到的索引。基于查询列和过滤条件进行判断。查询出来都会被列出来,但是不一定会是使用到。

如果在表中没有可用的索引,那么 key 列 展示 NULL,possible_keys 是 NULL,这说明查询没有覆盖到索引。

key_len 列

注意,在联合索引中,如果有 3 列,那么总字节是长度是 100 个字节的话,那么 key_len 值数据可能少于 100 字节,比如 30 个字节,这就说明了查询中并没有使用联合索引的所有列。而只是利用到某一些列或者 2 列。

key_len 的长度是由表中的定义的字段长度来计算的,并不是存储的实际长度,所以满足数据最短的实际字段存储,因为会直接影响到生成执行计划的生成 。

ref 列

指出那些列或常量被用于索引查找

rows 列

  1. 根据统计信息预估的扫描行数。

  2. 关联查询内嵌的次数,每获取匹配一个值都要对目标表查询,所以循环次数越多性能越差。

filtered 列

filtered 列跟 rows 列是有关联的,是返回预估符合条件的数据集,再去取的行的百分比。也是预估的值。数值越高查询性能越好。

Extra 列

如果 Extra 列中不显示任何信息,通常不需要专门进行优化,因为这说明查询的执行计划中没有额外的操作或者优化策略。然而,这并不意味着查询一定是最优的,仍然可以通过其他方式进行性能优化。

也就是说即使 Extra 列为空,也建议进行综合性能优化,根据实际情况选择合适的优化策略,从而进一步提高查询性能。

  1. Distinct:优化 distinct 操作,在找到第一匹配的元组后即停止找同样值得动作

  2. Not exists:使用 not exisits 来优化查询

NOT EXISTS 操作可能会导致查询性能下降,因为它通常需要执行子查询,并对结果集进行对比操作,这可能会涉及到大量的数据操作,特别是在处理大数据量的情况下

优化的方向可以从这几个方面考虑

  • 优化子查询:子查询是 NOT EXISTS 操作的核心,因此可以考虑优化子查询的性能。可以检查子查询的查询条件、索引使用情况、子查询中是否存在不必要的数据处理等,从而提高子查询的执行性能。

  • 使用其他操作替代 NOT EXISTS:有时候,可以通过其他方式替代 NOT EXISTS 操作,例如使用 LEFT JOIN 和 IS NULL 结合,或者使用 NOT IN 等操作。这些替代方式可能在某些情况下性能更好,可以根据实际情况进行选择。

  • 调整 MySQL 配置参数:MySQL 有一些与查询性能相关的配置参数,例如 join_buffer_size、max_join_size 等,可以根据实际情况适当调整这些参数,从而提高查询性能。

  1. Using filesort:使用文件来进行排序,通常会出现在 order by 或 group by 查询中

Using filesort 是 MySQL 查询执行计划中的一部分,表示查询需要按照某个列或多个列进行排序,而且可能无法使用内存进行排序,因此需要使用磁盘临时文件来处理排序操作,这通常会导致查询性能较低。

  1. Using index:使用了覆盖索引进行查询【查询所需要的信息用所用来获取,不需要对表进行访问

表示查询使用了索引来加速查询,这通常是一个好的现象,表示查询性能可能较好,无需特别优化

  1. Using temporary:MySQL需要使用临时表来处理,常见于排序、子查询和分组查询

需要优化

  1. Using where:需要在 MySQL 服务器层使用 where 条件来过滤数据

表示在执行查询时需要 MySQL 引擎层面进行条件筛选而不是通过索引等快速定位数据。这可能会导致查询性能下降,特别是在处理大量数据时。

优化的方向可以从这几个方面考虑:

  • 确认是否选择合适的索引

  • 通过优化检索条件避免不必要的查询、减少返回列数等方式

  • 使用 MySQL 查询缓存机制

  • 表结构是否合理有没有存在不合理的索引

  1. select tables optimized away:直接通过索引来获取数据,不用访问表