Mysql执行计划字段解释

本文详细介绍了如何查看MySQL查询的执行计划,以及执行计划中select_type和type字段的含义。通过对不同类型的查询示例,如简单查询、子查询、联合查询等的分析,帮助读者理解查询优化和性能提升的关键点。
摘要由CSDN通过智能技术生成

一、前言

每次遇到 sql 优化,查看执行计划,其中的字段是什么含义总是会忘,所以有了这篇文章方便查阅。

二、如何查看执行计划

第一种方法,在 SELECT 语句前面加上 EXPLAIN 关键字

image-20230720151221683

第三种方法,如果用 Navicat 数据库工具的话,点击上面的解释按钮

image-20230720151150027

三、执行计划各字段解释

先贴上 Mysql 官方文档中关于执行计划字段解释的地址:

官网:MySQL 8.0 Reference Manual / Optimization / Understanding the Query Execution Plan / EXPLAIN Output Format

中文网:MySQL 8.0 参考手册 / 第8章优化 / 8.8 了解查询执行计划 / 8.8.2 EXPLAIN 输出格式

字段说明
id操作的唯一标识符
select_type操作的类型
table涉及的表名
partitions操作涉及的分区
type表示使用的连接类型或扫描类型
possible_keys可能使用的索引列表
key实际选择使用的索引
key_len索引键的长度
ref连接条件所使用的列或常量
rows估计的扫描行数
filtered从结果中过滤返回行的百分比
Extra额外的信息

下面就结合一些实际例子看下 select_type、type、Extra

准备脚本如下:

-- 创建用户表
CREATE TABLE user (
id  int(11) NOT NULL AUTO_INCREMENT ,
name  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
age  int(11) NULL DEFAULT NULL ,
dept_id  int(11) NULL DEFAULT NULL ,
remark  text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
PRIMARY KEY (id),
INDEX index_name_age (name, age) USING BTREE ,
INDEX index_dept_id (dept_id) USING BTREE ,
FULLTEXT INDEX index_remark (remark) 
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=38
ROW_FORMAT=DYNAMIC
;

-- 初始化用户数据
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('1', '张三', '20', '1', 'abc');
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('2', '李四', '18', '1', 'bcd');
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('3', '王五', '22', '1', 'cde');
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('4', '赵六', '23', '2', 'def');
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('5', '孙七', '25', '2', 'efg');
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('6', '周八', '16', '2', 'fgh');
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('7', '吴九', '19', '3', 'ghi');
INSERT INTO user (id, name, age, dept_id, remark) VALUES ('8', '郑十', '13', '3', 'hij');

-- 创建部门表
CREATE TABLE dept (
id  int(11) NOT NULL ,
name  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (id),
INDEX index_name (name) USING BTREE 
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=DYNAMIC
;

-- 初始化部门数据
INSERT INTO dept (id, name) VALUES ('1', '人事部');
INSERT INTO dept (id, name) VALUES ('2', '财务部');
INSERT INTO dept (id, name) VALUES ('3', '技术部');

四、select_type

select_type说明
SIMPLE简单查询
PRIMARY主查询(外部查询)
SUBQUERY子查询
DEPENDENT SUBQUERY依赖子查询
UNCACHEABLE SUBQUERY不可缓存子查询
UNION合并查询
UNION RESULT合并查询结果
DEPENDENT UNION依赖合并查询
UNCACHEABLE UNION不可缓存合并查询
DERIVED派生表
MATERIALIZED物化

下面针对各种情况举例,不用纠结sql本身的业务含义

4.1、SIMPLE(简单查询)

不包含UNION或者子查询

4.1.1、简单的单表查询

EXPLAIN SELECT * FROM user;

image-20230720182617026

4.1.2、多表连接查询

EXPLAIN SELECT
	a.*, b.*
FROM
	USER a
INNER JOIN dept b ON b.id = a.dept_id;

4.2、PRIMARY(主查询)

包含复杂子查询的外层查询,或者UNION语句中的第一个查询

4.2.1、包含复杂子查询的外层查询

EXPLAIN SELECT
	*
FROM
	(
		SELECT
			*
		FROM
			USER
		WHERE
			id = 1
		UNION ALL
			SELECT
				*
			FROM
				USER
			WHERE
				id = 2
	) x;

image-20230721111030923

4.2.2、UNION语句中的第一个查询

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	id = 1
UNION ALL
	SELECT
		*
	FROM
		USER
	WHERE
		id = 2;

image-20230721111225371

4.3、SUBQUERY(子查询)、DEPENDENT SUBQUERY(依赖子查询)、UNCACHEABLE SUBQUERY(不可缓存子查询)

  • SUBQUERY(子查询):子查询是指嵌套在主查询中的查询语句。当 Mysql 优化器将子查询作为单独的查询执行,并将结果作为主查询的条件之一时,select_type 会被标记为 SUBQUERY 。这种情况下,子查询会在主查询之前执行,并将结果传递给主查询使用。
  • DEPENDENT SUBQUERY(依赖子查询):当一个子查询需要根据外部查询的结果来确定查询条件或提供必要的数据时,它就被称为依赖子查询。依赖子查询会在外部查询的每一行上执行,并根据外部查询的结果进行计算。
  • UNCACHEABLE SUBQUERY(不可缓存子查询):优化器可以根据查询中的各种条件和索引信息来对查询进行优化,以获得更好的性能。然而,某些类型的子查询由于其特殊的性质,无法被优化器缓存。

4.3.1、SUBQUERY(子查询)

如果是SUBQUERY(子查询)需要满足以下条件:

  1. 子查询不能转化为半连接,关于半连接可参考:8.2.2.1 使用半连接转换优化 IN 和 EXISTS 子查询谓词
  2. 子查询是不相关子查询

这里补充下不相关子查询相关子查询的含义

不相关子查询:子查询与主查询之间没有依赖关系,独立于主查询执行,只执行一次,然后将结果用于主查询的条件或者计算

相关子查询:子查询与主查询之间存在依赖关系,子查询的结果依赖于主查询的每一行数据

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	age > (SELECT avg(age) FROM USER);

image-20230724133742111

4.3.2、DEPENDENT SUBQUERY(依赖子查询)

如果是DEPENDENT SUBQUERY(依赖子查询)需要满足以下条件:

  1. 子查询不能转化为半连接
  2. 子查询是相关子查询

只要把上面例子里的 > 改成 IN,子查询就会变成相关子查询

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	age IN (SELECT avg(age) FROM USER);

image-20230724144341329

4.3.3、UNCACHEABLE SUBQUERY(不可缓存子查询)

当子查询中使用了不支持查询缓存的函数,会导致该子查询被标记为不可缓存

EXPLAIN SELECT
	(
		SELECT
			id
		FROM
			USER
		ORDER BY
			RAND()
		LIMIT 1
	) AS random_user
FROM
	DUAL;

image-20230724150507705

4.4、UNION(合并查询)、UNION RESULT(合并查询结果)、DEPENDENT UNION(依赖合并查询)、UNCACHEABLE UNION(不可缓存合并查询)

  • UNION(合并查询):在 UNION 语句中,第二个及之后的 select 会标记成 UNION。
  • UNION RESULT(合并查询结果):当执行 UNION 操作时,MySQL 可能会生成一个 UNION RESULT 来存储合并后的结果集,以供后续处理使用,这个结果集就会标记为 UNION RESULT。
  • DEPENDENT UNION(依赖合并查询):依赖于外部查询的 UNION 操作,UNION 的结果取决于外部查询的结果,无法独立地进行计算,通常表现为子查询中,第二个及之后的 select。
  • UNCACHEABLE UNION(不可缓存合并查询):和 UNCACHEABLE SUBQUERY(不可缓存子查询)类似,表示一个无法通过查询缓存来进行优化的 UNION 操作。

4.4.1、UNION(合并查询)、UNION RESULT(合并查询结果)

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	id = 1
UNION
	SELECT
		*
	FROM
		USER
	WHERE
		id = 2;

image-20230721142623017

4.4.2、DEPENDENT UNION(依赖合并查询)

当UNION作为子查询时,第二个或者后面的查询语句

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	id IN (
		SELECT
			id
		FROM
			USER
		WHERE
			NAME = '张三'
		UNION ALL
			SELECT
				id
			FROM
				USER
			WHERE
				NAME = '李四'
	);

image-20230725110243771

4.4.3、UNCACHEABLE UNION(不可缓存合并查询)

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	id IN (
		SELECT
			id
		FROM
			USER
		WHERE
			NAME = '张三'
		UNION
			SELECT
				id
			FROM
				USER
			WHERE
				NAME <> '张三'
			ORDER BY
				RAND()
	);

image-20230725110331726

4.5、DERIVED(派生表)

在查询中使用子查询生成的临时表

EXPLAIN SELECT
	u.dept_id,
	u.avgAge
FROM
	(
		SELECT
			dept_id,
			avg(age) avgAge
		FROM
			USER
		WHERE
			id > 3
		GROUP BY
			dept_id
	) AS u
WHERE
	u.avgAge > 20;

image-20230724174348276

4.6、MATERIALIZED(物化)

当查询包含子查询或派生表,并且优化器认为将其结果保存到临时表中会更有效率时,就会使用物化表。使用物化表可以避免在每次引用子查询或派生表时都需要重新计算结果集,从而提高查询性能。特别是当子查询或派生表的结果集较大或计算复杂时。

五、type

type说明
system该表只有一行(相当于系统表),system是const类型的特例
const键或唯一索引的等值查询,最多只返回一行数据
eq_ref当使用了索引的全部组成部分,并且索引是PRIMARY KEY或UNIQUE NOT NULL 才会使用该类型
ref如果仅使用了索引的最左边前缀,或者索引不是PRIMARY KEY或UNIQUE
fulltext全文索引
ref_or_null和ref类似,但是会额外搜索哪些行包含了NULL
index_merge使用索引合并优化
unique_subquery和eq_ref类似,但是使用了IN查询,且子查询是主键或者唯一索引
index_subquery和unique_subquery类似,只是子查询使用的是非唯一索引
range仅检索给定范围内的行,使用索引来选择行,可以在使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, 或 IN()等操作符。
index与ALL类似,只是扫描了索引树
ALL全表扫描

性能从优到劣依次为:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

5.1、system

该表只有一行(相当于系统表),system是const类型的特例

5.2、const

键或唯一索引的等值查询,最多只返回一行数据

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	id = 1;

image-20230725102043213

5.3、eq_ref

当使用了索引的全部组成部分,并且索引是PRIMARY KEY或UNIQUE NOT NULL 才会使用该类型

EXPLAIN SELECT
	u.*, d.*
FROM
	USER u
INNER JOIN dept d ON d.id = u.dept_id
WHERE
	u. NAME = '张三';

image-20230725110040061

5.4、ref

如果仅使用了索引的最左边前缀,或者索引不是PRIMARY KEY或UNIQUE

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	NAME = '张三';

image-20230725110123874

5.5、fulltext

全文索引

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	MATCH (remark) AGAINST ('c*' IN boolean MODE);

image-20230725104629982

5.6、ref_or_null

和ref类似,但是会额外搜索哪些行包含了NULL

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	NAME = 'zhangsan'
OR NAME IS NULL;

image-20230725105121498

5.7、index_merge

使用索引合并优化

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	id = '1'
OR NAME = '张三';

image-20230725110842295

5.8、unique_subquery

和eq_ref类似,但是使用了IN查询,且子查询是主键或者唯一索引

5.9、index_subquery

和unique_subquery类似,只是子查询使用的是非唯一索引

5.10、range

仅检索给定范围内的行,使用索引来选择行,可以在使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, 或 IN()等操作符。

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	id > 5

image-20230725131152637

5.11、index

与ALL类似,只是扫描了索引树

5.12、ALL

全表扫描

六、Extra

这里列出一些常见的,其他的参考官方文档

6.1、Using index

查询使用了覆盖索引,查询的所有列都可以从索引中获取,而不需要回表查询数据行。

EXPLAIN SELECT
	id
FROM
	USER
WHERE
	id = 1;

image-20230725140007277

6.2、Using where

在执行查询时会使用WHERE子句对结果进行进一步的筛选,或者全表扫描

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	age > 18;

image-20230725140306128

6.3、Using temporary

为了解析查询,MySQL 需要创建一个临时表来保存结果。

EXPLAIN SELECT
	NAME
FROM
	USER
WHERE
	id = 1
UNION
	SELECT
		NAME
	FROM
		USER
	WHERE
		id = 2;

image-20230725140533806

6.4、Using filesort

无法使用索引或其他优化方式直接按照查询语句中的顺序返回结果,而是需要额外的排序操作。会对性能产生一定影响,特别是对大数据量的查询语句。

EXPLAIN SELECT
	*
FROM
	USER
ORDER BY
	age
LIMIT 10;

image-20230725140628343

6.5、Using index condition

表示查询使用了索引条件过滤数据,通常表示使用了索引下推,是一个好的优化迹象

索引下推:指 MySQL 将 WHERE 条件中可用于索引的部分推到存储引擎层级进行处理,而不是等到存储引擎返回数据行后再应用 WHERE 条件进行过滤。这样可以减少 IO 操作和数据传输,提高查询性能。

EXPLAIN SELECT
	*
FROM
	USER
WHERE
	NAME = '张三'
AND age > 18;

image-20230725140242178

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天进步亿点点的小码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值