MySQL EXPLAIN命令的总结

本文深入解析MySQL查询优化器的工作原理,特别是通过Explain命令展示SQL执行计划中的各项指标含义,帮助开发者理解如何优化SQL查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在看一本书《高性能mysql》,这本书是一本经典的书,我通过这本书解决了许多实际开发中的问题,从建库到横向硬件的优化应有尽有。

这里总结一下sql语句Explain查询优化器的一些内容,主要是通过实例来解释命令后的每一行所输出每一列所代表的含义。

先看一下EXPLAIN后所有的列
这里写图片描述

id:

标识select所属的行,也就是第几个select子查询,如果没有子查询或者union查询,则所有的select id都是1

1).没有子查询或者 union查询

EXPLAIN select * from user_point up 
LEFT JOIN user_point_history uph on up.id=user_point_id;

这里写图片描述

2).from前的子查询

第一个查询是最外层的查询,通过table列也能看到,第二个查询是from前的查询

EXPLAIN select uph.transaction_point
	,(select up.point from user_point up where up.id=uph.user_point_id) 
from user_point_history uph;

这里写图片描述

3).from后的子查询(派生表)

第二个查询是user_point,所以确定第一个查询是最外层的查询,至于table为什么是derived2这个在说table列的时候再细说。

EXPLAIN select * from (select * from user_point) up 

这里写图片描述

4).UNION查询

union输出的额外的行,也就是联合查询后的结果,一般都是放在匿名表中,之后mysql将结果读取到临时表中。临时表不再sql中出现,因此id是null,结果产生在最后因此它也是在最后的位置。
列表内容

EXPLAIN 
select * from user_point u1
	UNION
select * from user_point u2

这里写图片描述

select_type:

描述是简单查询,还是复杂查询。主要属性如下:

1).simple:简单的select查询,不包含子查询或者联合查询

EXPLAIN select * from user_point up 
LEFT JOIN user_point_history uph on up.id=user_point_id;

这里写图片描述

2).primary:如果有子查询那么最外层(或者最开始)的select是primary类型

EXPLAIN select * from (select * from user_point) up 

这里写图片描述

3).subquery:一般是from 之前子查询,也就是select列表中的查询

EXPLAIN select (select id from user_point_history LIMIT 1) from user_point ;

这里写图片描述

另外还有一种情况是DEPENDENT SUBQUERY,这种情况是列表子查询中的依赖最外层查询的数据了

EXPLAIN select 
	uph.transaction_point
	,(select up.point from user_point up where up.id=uph.user_point_id) 
from user_point_history uph;

这里写图片描述

4).drived:FROM后子查询select会出现此种类型,mysql会递归并将结果放到一个临时表中吗,服务器内部称为派生表,因此该临时表是从子查询中派生而来的

EXPLAIN select * from (select * from user_point) up 

这里写图片描述

5).union/union result:在union后紧跟的查询,标记union类型,从union的匿名标检索结果的select被标记为union result

EXPLAIN 
select * from user_point u1
	UNION
select * from user_point u2

这里写图片描述

table:

这一列显示对应了正在访问那张表,主要分成三种分类

1).无派生标和联合查询

EXPLAIN select (select id from user_point_history LIMIT 1) from user_point ;

这里写图片描述

2).派生表查询
派生表(from 后的select查询)的外层查询的table列是’drivedN’的形式,N是子查询的ID,N指向的是EXPLAN输出后面的一行,也就是这个派生表的查询

EXPLAIN select (select id from user_point_history LIMIT 1) from (select * from user_point) up ;

这里写图片描述

3).UNION联合查询
table列中的‘ union1,2’指向的是参与联合查询的id

EXPLAIN 
select * from user_point u1
	UNION
select * from user_point u2

这里写图片描述

type:

关键的一列,决定了如何查找表中的行,下面的性能从差到优

1).ALL:全表扫描,意味着mysql会扫描整张表,如果使用了limit效率会提高,或者在Extra列中显示"USING distinct/not exists"。

EXPLAIN select * from user_point_history	

这里写图片描述

2).Index 这个跟全表扫描一样,主要查询是按照MYSQL索引次序进行,它的优点是避免了排序,而缺点是要承担按照索引次序排序读取整个表的开销;

EXPLAIN select * from user_point_history ORDER BY id desc	

这里写图片描述

如果在extra列看到了using index,则mysql正在使用覆盖索引,它只扫描索引的数据,而不是按照索引次序的每一行,它比次序全表扫描的开销要少很多

EXPLAIN select id from user_point_history	

这里写图片描述

3).range:范围扫描,它开始于索引里的某一点,返回匹配这个值域的行。这比全表扫描要好一点。用不着遍历全部索引了。这种是where 子句中带有 between 或者 ><

	EXPLAIN select id from user_point_history where id>''	

这里写图片描述

利用索引去查找一系列值时,例如IN() 或者OR 列表,也会显示为范围扫描。

EXPLAIN select id from user_point_history where id in ('1','2','3') or id>''	

这里写图片描述

4).ref 这是一种索引访问,它可能会找到多个符合条件的行,因此它是查找和扫描的混合体。此类索引访问只有当前使用非唯一索引,或者唯一索引的非唯一性前缀才会出现。
注:此表中user_id和invest_id是联合唯一索引。这里符合”唯一索引的非唯一性前缀才会出现“

EXPLAIN SELECT * FROM `task_invest` ti where ti.user_id='';	

5).eq_ref 使用这种索引查找,mysql最多会返回一条符合条件的记录。这种访问方法可以使用主键或者唯一索引查找时。它会将它们与某个参考值做比较

6).const,system 当mysql能对查询的部分进行优化,将其转换为一个常量时,它就会使用这些访问类型。举例:如果通过某一行的主键放入where子句里的方式来选取此行的主键,mysql能把这个查询转换为一个常量

possible_key:

这一列显示了查询可以使用哪些索引,这是基于访问的列和使用比较操作符来判断的,这个列在优化过程的早期创建的,因此有些罗列出来的索引可能后期优化过程是没用的

key:

mysql决定用那个索引来访问。

key_len:

使用索引的字节数,比如user表的主键user_id 是varchar(32)则这里的长度是98

ref:

指向key列中索引查询用到的列或常量

row:

估计为了找到所需的行需要检查读取的行数。不是结果集的行数。
这里写图片描述
这个需要检查的列是663*9704

filtered

它显示的针对表里符合查询某个条件的记录的百分比所做的一个悲观估算,需要将row列数据与其相乘,就能看到mysql估算它将和查询计划里前一个表的行数
举例:表中有1000条数据,id自增长,从1到1000,当查询id<500的数据时,此列值为50,row列是1000,则可以估算访问到的数据为500左右。

### MySQLEXPLAIN 命令的使用说明 #### 什么是 EXPLAIN? `EXPLAIN` 是一种用于分析 SQL 查询性能的重要工具。它能够展示查询的执行计划,帮助开发者理解数据库如何处理特定的查询请求。通过 `EXPLAIN` 的输出,可以判断查询是否高效,是否存在未利用索引的情况,以及是否有不必要的全表扫描等问题。 --- #### 输出字段解释 以下是 `EXPLAIN` 命令的主要输出字段及其含义: 1. **id** - 这一列表示查询中每个 SELECT 子句的序列号。如果存在子查询或多表连接,则可能会有多个 id 值[^1]。 2. **select_type** - 描述查询的具体类型。常见的值包括: - SIMPLE:简单查询,不包含子查询或联合。 - PRIMARY:最外层的查询。 - SUBQUERY:在 SELECT 或 WHERE 列表中的子查询。 - DERIVED:派生表(即 FROM 子句中的子查询)。 - UNION:表示第二个或后续的 UNION 成分[^3]。 3. **table** - 显示当前正在操作的数据表名称。如果有 JOIN 操作,这里会列出参与联接的所有表名。 4. **partitions** - 如果启用了分区功能,这一列将显示匹配哪些分区。如果没有启用分区,则此列为 NULL。 5. **type** - 表明访问数据的方式,按效率从高到低排列如下: - system/const:单行读取。 - eq_ref:唯一性索引查找。 - ref:非唯一性索引查找。 - range:范围扫描。 - index:全索引扫描。 - ALL:全表扫描(通常是最差的选择)。 6. **possible_keys** - 列出了 MySQL 能够用来寻找记录的候选索引集合。注意,这里的索引不一定都会被真正使用[^2]。 7. **key** - 实际上由 MySQL 决定使用的索引。如果没有选择任何索引,则其值为 NULL。可以通过提示关键字 FORCE INDEX、USE INDEX 或 IGNORE INDEX 来影响最终决策。 8. **key_len** - 所选索引占用字节数量。一般而言,较短的关键长度更优。 9. **ref** - 展示了与 key 字段对应的引用项,可能是某个常数或者是另一张表格里的某一栏位。 10. **rows** - 预估为了完成此次查询所需要检查的总行数。数值越少越好。 11. **filtered** - 经过条件过滤之后剩余的比例百分比。接近于 100% 更理想。 12. **Extra** - 提供额外的信息,比如排序方式、临时文件创建情况等。某些常见标志包括 Using where, Using filesort 和 Using temporary 等。 --- #### 如何使用 EXPLAIN? 要查看某条 SQL 语句的执行计划,只需在其前加上关键词 `EXPLAIN` 即可。例如: ```sql EXPLAIN SELECT * FROM users WHERE age > 30; ``` 上述例子将会返回关于这条查询是如何被执行的相关信息。 --- #### 示例代码演示 假设有一个名为 `employees` 的员工表结构如下: | Field | Type | |-------------|--------------| | emp_id | INT | | first_name | VARCHAR(50) | | last_name | VARCHAR(50) | | department | VARCHAR(50) | | salary | DECIMAL(10,2)| 现在想找出工资大于平均薪资的所有雇员名单并对其进行降序排列: ```sql EXPLAIN EXTENDED SELECT e.* FROM employees AS e WHERE e.salary > (SELECT AVG(salary) FROM employees); ``` 这段脚本不仅揭示基本的检索路径而且还提供了扩展版本里更多的内部计算详情。 --- #### 总结 掌握好 `EXPLAIN` 工具可以帮助我们快速定位那些表现不佳的查询,并采取适当措施加以改进。无论是调整现有索引还是重构复杂查询逻辑都能显著提升整体应用效能。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值