MySQL性能优化(七):MySQL执行计划,真的很重要,来一起学习吧

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 一条 SQL 被一个懵懂的少年,一阵蹂躏,扔向了 MySQL 服务器的尽头,少年苦苦等待,却迟迟等不来那满载而归的硕果。于是少年气愤,费尽苦心想从度娘那边寻求帮助,面对执行计划 EXPLAIN,却等来的是无尽的折磨与抓狂。

image.png

作者:xcbeyond

博客:https://xcbeyond.cn/ 公众号:程序猿技术大咖


一条 SQL 被一个懵懂的少年,一阵蹂躏,扔向了 MySQL 服务器的尽头,少年苦苦等待,却迟迟等不来那满载而归的硕果。于是少年气愤,费尽苦心想从度娘那边寻求帮助,面对执行计划 EXPLAIN,却等来的是无尽的折磨与抓狂。


通过 explain 命令,根据执行计划找到存在性能问题的 SQL 语句,以帮助我们优化 SQL 提供方向和依据。


如果面对执行计划,你也是一脸疑惑,甚至抓狂,那么你真的需要认真的来了解它了。在数据库性能优化中,执行计划,真的很重要,通过执行计划能够帮助我们更加明确的来进行 SQL 优化。本文将从执行计划开始说起,讲解执行计划该如何用,其中各个列的含义究竟是什么。


一、执行计划?

执行计划,就是一条 SQL 语句,在数据库中实际执行的时候,一步步的分别都做了什么。也就是我们用 EXPLAIN 分析一条 SQL 语句时展示出来的那些信息。


EXPLAIN 命令是查看查询优化器是如何决定执行查询的主要方法,从它的查询结果中可以知道一个 SQL 语句每一步是如何执行的,都经历了些什么,分为哪几步,有没有用到索引,哪些字段用到了什么样的索引,是否有一些可优化的地方等,这些信息都是我们SQL优化的依据。


要使用 EXPLAIN,只需在查询中的 SELECT 关键字之前增加 EXPLAIN。语法如下:


EXPLAIN + SELECT 查询语句;


当执行执行计划时,只会返回执行计划中每一步的信息,它会返回一行或多行信息,显示出执行计划中的每一部分和执行的次序。


如:


如果查询的是多个关联表,执行计划结果可能是多行。

在接下来涉及到的示例表,均来自于MySQL官方的示例数据库 sakila,脚本下载:https://downloads.mysql.com/docs/sakila-db.zip


二、执行计划中的列

EXPLAIN 的结果总是有相同的列,每一列代表着不同的含义,可变的只是行数和内容。从上面的例子中,我们看到返回的有很多列,为了更加清楚的了解每一列的含义,便于我们更好的完成优化 SQL。


涉及到的列有:

列名 含义
id id 列,表示查询中执行 select 子句或操作表的顺序。
select_type 查询类型,主要是用于区分普通查询、联合查询、子查询等复杂的查询。
table 表明对应行正在访问的是哪个表。
partitions 查询涉及到的分区。
type 访问类型,决定如何查找表中的行。
possible_keys 查询可以使用哪些索引。
key 实际使用的索引,如果为NULL,则没有使用索引。
key_len 索引中使用的字节数,查询中使用的索引的长度(最大可能长度),并非实际使用长度,理论上长度越短越好。
ref 显示索引的那一列被使用。
rows 估算出找到所需行而要读取的行数。
filtered 返回结果的行数占读取行数的百分比,值越大越好。
Extra 额外信息,但又十分重要。


1. id 列

id 列是一个编号,用于标识 SELECT 查询的序列号,表示执行 SQL 查询过程中 SELECT 子句或操作表的顺序。


如果在 SQL 中没有子查询或关联查询,那么 id 列都将显示一个 1。否则,内层的 SELECT 语句一般会顺序编号。


id 列分为三种情况:


1)id 相同

如下普通查询,没有子查询。

explain select f.* from film f,film_actor fa,actor a where f.film_id = fa.film_id and fa.actor_id = a.actor_id and a.first_name = 'NICK';


2)id 不同

如果存在子查询,id 的序号会递增,id 值越大优先级越高,越先被执行

explain select * from film where film_id = (select film_id from film_actor where actor_id = 2 limit 1);


3)id 相同又不同

1)、2)两种情况同时存在。id 如果相同,认为是一组,从从上往下执行。在所有组中,id 值越大,优先级越高,越先执行。


2. select_type 列

select_type 列表示对应行的查询类型,是简单查询还是复杂查询,主要用于区分普通查询、联合查询、子查询等复杂的查询。


select_type 列有如下值:

select_type值 说明
SIMPLE 简单查询,意味着不包括子查询或 UNION
PRIMARY 查询中包含任何复杂的子部分,最外层查询则被标记为 PRIMARY
SUBQUERY selectwhere 列表中包含了子查询
DERIVED 表示包含在 from 子句的子查询中的 select,MySQL 会递归执行并将结果放到一个临时表中,称其为“派生表”,因为该临时表是从子查询中派生而来的。
UNION 第二个 select 出现在 UNION 之后,则被标记为 UNION
UNION RESULT UNION 表获取结果的 select


3. table 列

table 列表示对应行正在执行的哪张表,指代对应表名,或者该表的别名(如果 SQL 中定义了别名)。


4. partitions 列

查询涉及到的分区。


5. type 列

type 列指代访问类型,是 MySQL 决定如何查找表中的行。


是 SQL 查询优化中一个很重要的指标,拥有很多值,依次从最差到最优:

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


1)ALL

众所周知的全表扫描,表示通过扫描整张表来找到匹配的行,很显然这样的方式查询速度很慢。


这种情况,性能最差,在写SQL时尽量避免此种情况的出现。


举例如下:

explain select * from film;


在平时写 SQL 时,避免使用 select *,就不难理解了。换言之,是为了避免全表扫描,因为全面扫描是性能最差的。


2)index

全索引扫描,和全表扫描 ALL 类似,扫描表时按索引次序进行,而不是按行扫描,即:只遍历索引树。


indexALL 虽然都是读全表,但 index 是从索引中读取,而 ALL 是从硬盘读取。显然,index 性能上优于 ALL合理的添加索引将有助于性能的提升


举例如下:

explain select title from film;
explain select description from film;



通过 explain 结果来看,只查询表 film 中字段 title 时,是按照索引扫描的(type 列为 index),倘若查询字段 description,却是按照全表扫描的(type 列为 ALL)。这是为何呢?


接下来,我们不妨看看表 film 的结构:


desc film 结果来看,字段 title 创建的有索引,而字段 description 没有,所以 select title from film 是按索引扫描,而 select description from film 按全表扫描。


从上面的举例对比中,也充分印证了索引的重要性。


3)range

只检索给定范围的行,使用一个索引来选择行。key 列显示使用了那个索引。一般就是在 where 语句中出现了 bettween、<、>、in 等的查询。这种索引列上的范围扫描比全索引扫描 index 要好。


举例如下:

explain select * from film where film_id between 1 and 10;


4)ref

非唯一性索引扫描,返回匹配某个单独值的所有行。本质是也是一种索引访问,它返回所有匹配某个单独值的行,然而它可能会找到多个符合条件的行,所以它属于查找和扫描的混合体。


此类型只有当使用非唯一索引或者唯一索引的非唯一性前缀时,才会发生。


举例如下:

show index from film;
explain select * from film where title = 'ACADEMY DINOSAUR';


5)eq_ref

唯一索引扫描。 常见于主键或唯一索引扫描。


6)const

通过索引一次就能找到,const 用于比较 primary key 或者 unique 索引。因为只需匹配一行数据,所有很快。如果将主键置于 where 列表中,MySQL 就能将该查询转换为一个 const


举例如下:

show index from film;
explain select * from film where film_id = 1;


7)system

表只有一行记录,这是 const 类型的特例,比较少见,如:系统表。


6. possible_keys 列

显示在查询中使用了哪些索引。


7. key 列

实际使用的索引,如果为 NULL,则没有使用索引。查询中如果使用了覆盖索引,则该索引仅出现在 key 列中。


possible_keys 列表明哪一个索引有助于更高效的查询,而 key 列表明实际优化采用了哪一个索引可以更加高效。


举例如下:

show index from film_actor;
explain select actor_id,film_id from film_actor;


8. key_len 列

表示索引中使用的字节数,查询中使用的索的长度(最大可能长度),并非实际使用长度,理论上长度越短越好。key_len 是根据表定义计算而得的,不是通过表内检索出的。


9. ref 列

表示在 key 列记录的索引中查找值,所用的列或常量 const


10. rows 列

估算出找到所需行而要读取的行数。


这个数字是内嵌循环关联计划里的循环数,它并不是最终从表中读取出来的行数,而是 MySQL 为了找到符合查询的那些行而必须读取行的平均数,只能作为一个相对数来进行衡量。


11. filtered 列

返回结果的行数占读取行数的百分比,值越大越好。


举例如下:


表 film_actor中actor_id 为 1 的记录有 19 条,而 SQL 查询时扫描了 19 行(rows:19),19 条符合条件(filtered: 100 19/19)


12. Extra 列

额外信息,但又十分重要。


常见的值如下:

1)Using index

表示 SQL 中使用了覆盖索引。


举例如下:


2)Using where

许多 where 条件里是涉及索引中的列,当它读取索引时,就能被存储引擎检验,因此不是所有带 where 子句的查询都会显示 Using where


3)Using temporary

对查询结果排序时,使用了一个临时表,常见于 order bygroup by


4)Using filesort

对数据使用了一个外部的索引排序,而不是按照表内的索引进行排序读取。也就是说 MySQL 无法利用索引完成的排序操作成为“文件排序”。


三、总结

通过上述对执行计划的了解,我们能够从中得到什么?


  • SQL 如何使用索引
  • 复杂 SQL 的执行顺序
  • 查询扫描的数据函数
  • ……


当面临不够优的 SQL 时,我们首先要查看其执行计划,根据执行计划结果来分析可能存在哪些问题,从而帮助、指导我们是否添加索引、是否调整 SQL 顺序、是否避免不应该的书写方式等等。


以上就是这篇文章的全部内容,希望本文的内容对大家在SQL性能优化、SQL 书写时,有一定的帮助。


执行计划,真的很重要,尤其是 SQL 调优时,很香!

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
4月前
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
612 5
|
5月前
|
关系型数据库 MySQL 数据管理
Mysql基础学习day03-作业
本内容包含数据库建表语句及多表查询示例,涵盖内连接、外连接、子查询及聚合统计,适用于员工与部门数据管理场景。
101 1
|
5月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day01
本课程为MySQL基础学习第一天内容,涵盖MySQL概述、安装、SQL简介及其分类(DDL、DML、DQL、DCL)、数据库操作(查询、创建、使用、删除)及表操作(创建、约束、数据类型)。适合初学者入门学习数据库基本概念和操作方法。
218 6
|
12月前
|
SQL 关系型数据库 MySQL
MySQL进阶突击系列(07) 她气鼓鼓递来一条SQL | 怎么看执行计划、SQL怎么优化?
在日常研发工作当中,系统性能优化,从大的方面来看主要涉及基础平台优化、业务系统性能优化、数据库优化。面对数据库优化,除了DBA在集群性能、服务器调优需要投入精力,我们研发需要负责业务SQL执行优化。当业务数据量达到一定规模后,SQL执行效率可能就会出现瓶颈,影响系统业务响应。掌握如何判断SQL执行慢、以及如何分析SQL执行计划、优化SQL的技能,在工作中解决SQL性能问题显得非常关键。
|
5月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day02-作业
本教程介绍了数据库表的创建与管理操作,包括创建员工表、插入测试数据、删除记录、更新数据以及多种查询操作,涵盖了SQL语句的基本使用方法,适合初学者学习数据库操作基础。
121 0
|
5月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day03
本课程为MySQL基础学习第三天内容,主要讲解多表关系与多表查询。内容涵盖物理外键与逻辑外键的区别、一对多、一对一及多对多关系的实现方式,以及内连接、外连接、子查询等多表查询方法,并通过具体案例演示SQL语句的编写与应用。
155 0
|
5月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day01-作业
本教程包含三个数据库表的创建练习:学生表(student)要求具备主键、自增长、非空、默认值及唯一约束;课程表(course)定义主键、非空唯一字段及数值精度限制;员工表(employee)包含自增主键、非空字段、默认值、唯一电话号及日期时间类型字段。每个表的结构设计均附有详细SQL代码示例。
108 0
|
5月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day02
本课程为MySQL基础学习第二天内容,涵盖数据定义语言(DDL)的表查询、修改与删除操作,以及数据操作语言(DML)的增删改查功能。通过具体SQL语句与实例演示,帮助学习者掌握MySQL表结构操作及数据管理技巧。
179 0
|
7月前
|
存储 SQL 关系型数据库
MySQL 核心知识与性能优化全解析
我整理的这份内容涵盖了 MySQL 诸多核心知识。包括查询语句的书写与执行顺序,多表查询的连接方式及内、外连接的区别。还讲了 CHAR 和 VARCHAR 的差异,索引的类型、底层结构、聚簇与非聚簇之分,以及回表查询、覆盖索引、左前缀原则和索引失效情形,还有建索引的取舍。对比了 MyISAM 和 InnoDB 存储引擎的不同,提及性能优化的多方面方法,以及超大分页处理、慢查询定位与分析等,最后提到了锁和分库分表可参考相关资料。
163 0
|
缓存 关系型数据库 MySQL
MySQL执行计划选择策略:揭秘查询优化的艺术
【10月更文挑战第15天】 在数据库性能优化中,选择最优的执行计划是提升查询效率的关键。MySQL作为一个强大的关系型数据库管理系统,提供了复杂的查询优化器来生成执行计划。本文将深入探讨如何选择合适的执行计划,以及为什么某些计划更优。
400 2

推荐镜像

更多