Mysql基础(十六):explain命令SQL执行计划
目录
写在前面
1、通过explain命令得到的SQL执行计划(1)
2、通过explain命令得到的SQL执行计划(2)
3、通过explain命令得到的SQL执行计划(3)
4、通过explain命令得到的SQL执行计划(4)
5、type取值
6、possible_keys/ref取值
7、extra取值
8、Using where/join buffer
9、Using filesore/Using tempory
写在前面
每条SQL语句,mysql都会经过成本和规则的优化,对这个SQL选择对应的一些访问方法和顺序,包括做一些特殊的改写确保执行效率是最优的,然后优化过后,就会得到一个执行计划。
1、通过explain命令得到的SQL执行计划(1)
explain拿到这个SQL语句的执行计划:
explain select * from table
![](/assets/blank.gif)
- 如果是一个简单的单表查询,可能这里就只有一条数据,代表他打算如何访问这一个表。
- 但是SQL语句极为复杂,可能这里会有很多条数据,因为一个复杂的SQL语句的执行是要拆 分为很多步骤的,比如先访问表A,接着搞一个排序,然后来一个分组聚合,再访问表B,接着搞一个连接。
- 1、id:每个SELECT都会对应一个id,就是一个复杂的SQL里可能会有很多个SELECT,也可能会包含多条执行计划,每一条执行计划都会有一个唯一的id。
- 2、select_type:就是这一条执行计划对应的查询是个什么查询类型。
- 3、table:表名,意思是要查询哪个表。
- 4、partitions:是表分区的概念。
- 5、type:针对当前这个表的访问方法,比如说const、ref、range、index、all之类的,分别代表了使用聚簇索引、二级索引、全表扫描之类的访问方式。
- 6、possible_keys:跟type结合起来的,意思就是说你type确定访问方式了,那么到底有哪些索引是可供选择,可以使用的呢,这都会放这里。
- 7、key:就是在possible_keys里实际选择的那个索引。
- 8、key_len:就是索引的长度。
- 9、ref:就是使用某个字段的索引进行等值匹配搜索的时候,跟索引列进行等值匹配的那个目标值的一些信息。
- 10、rows:是预估通过索引或者别的方式访问这个表的时候,大概可能会读取多少条数。
- 11、filtered:就是经过搜索条件过滤之后的剩余数据的百分比。
- 12、extra:是一些额外的信息。
2、通过explain命令得到的SQL执行计划(2)
explain select * from t1
![](/assets/blank.gif)
select_type是SIMPLE表示查询类型是很普通的且简单。表名就是t1,all就是全表扫描,这没办法,你完全没加任何where条件,那当然只能是全表扫描了!这里直接会扫描表的聚簇索引的叶子节点,按顺序扫描过去拿到表里全部数据。rows是3457,这说明全表扫描会扫描这个表的3457条数据,说明这个表里就有3457条数据,此时你全表扫描会全部扫描出来。filtered是100%,你没有任何where过滤条件,所以直接筛选出来的数据就是表里数据的100%占比。
2、多表查询:
explain select * from t1 join t2
- extra里说了是Nested Loop,也就是嵌套循环的访问方式。
- 上面两条执行计划的id都是1,实际上一般来说,在执行计划里,一个SELECT会对应一个id,因为这两条执行计划对应的是一个SELECT语句,所以他们俩的id都是1,是一样。
- 如果有一个子查询,有另外一个SELECT,那么另外一个SELECT子查询对应的执行计划的id就可能是2了。
3、通过explain命令得到的SQL执行计划(3)
EXPLAIN SELECT * FROM t1 WHERE x1 IN (SELECT x1 FROM t2) OR x3 = 'xxxx';
![](/assets/blank.gif)
- 首先,第一条执行计划的id是1,第二条执行计划的id是2,因为SQL里有两个SELECT,主查询SELECT的执行计划的id就是1,子查询SELECT的执行计划的id就是2 。
- 其次,第一条执行计划里,select_type是PRIMARY,不是SIMPLE了,主查询的意思。
- 对主查询而言,他有一个where条件是x3='xxx',所以他的possible_keys里包含了index_x3,就是x3字段的索引,但是他的key实际是NULL,而且type是ALL,所以说他最后没选择用x3 字段的索引,而是选择了全表扫描。这是为什么呢?其实很简单,可能他通过成本分析发现,使用x3字段的索引扫描xxx这个值,几乎就跟全表扫描差不多,可能x3这个字段的值几乎都是xxx,所以最后就选择还不如直接全表扫描呢。
- 接着,第二条执行计划,他的select_type是SUBQUERY,也就是子查询,子查询针对的是t2这个表,当然子查询本身就是一个全表查询,但是对主查询而言,会使用x1 in 这个筛选条件
- type是index,说明使用了扫描index_x1这个x1字段的二级索引的方式,直接扫描x1字段的二级索引,来跟子查询的结果集做比对。
4、Union查询
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2
union字句默认的作用是把两个结果集合并起来还会进行去重,所以第三条执行计划就是去重。
- table是<union 1,2>,这就是一个临时表的表名。
- extra里有一个using temporary,也就是使用临时表的意思,他就是把结果集放到临时表里进行去重的。
- 当然,如果你用的是union all,那么就不会进行去重了。
4、通过explain命令得到的SQL执行计划(4)
- id:一个SELECT子句对应一个id,如果有多个SELECT就会对应多个id。但有时候一个SELECT字句涉及多个表,所以会对应多条执行计划,此时可能多条执行计划的id一样。
- select_type:SIMPLE、primary和subquery的。
- 一般如果单表查询或者是多表连接查询,其实他们的select_type都是SIMPLE。
- 如果是union语句的话,就类似于select * from t1 union select * from t2,那么会对应两条执行计划,第一条执行计划是针对t1表的,select_type是PRIMARY,第二条执行计划是针对t2表的,select_type是UNION,这就是在出现union语句的时候,他们就不一样了。
- 在使用union语句的时候,会有第三条执行计划,意思是针对两个查询的结果依托一个临时表进行去重,第三条执行计划的select_type就是union_result。
- 如果SQL里有子查询的话,类似select * from t1 where x1 in (select x1 from t2) or x3='xxx',此时其实会有两条执行计划,第一条执行计划的select_type是PRIMARY,第二条执行计划的select_type是SUBQUERY。
5、复杂SQL查询
EXPLAIN SELECT * FROM t1 WHERE x1 IN (SELECT x1 FROM t2 WHERE x1 = 'xxx' UNION SELECT x1 FROM t1 WHERE x1 = 'xxx');
- 第一个执行计划一看就是针对t1表查询的那个外层循环,select_type就是PRIMARY,因为这里涉及到了子查询,所以外层查询的select_type一定是PRIMARY了。
- 第二个执行计划是子查询里针对t2表的查询语句,他的select_type是dependent subquery。
- 第三个执行计划是子查询里针对t1表的另外一个查询语句,select_type是dependent union,因为第三个执行计划是在执行union后的查询。
- 第四个执行计划select_type是union result,因为在执行子查询里两个结果集的合并以及去重
6、更复杂SQL查询
explain select * from (select x1, count(*) as cnt from t1 group by x1) as _t1 where cnt > 10;
FROM子句后跟了一个子查询,在子查询里是根据x1字段进行分组然后进行count聚合操作,也就是统计出来x1这个字段每个值的个数,然后在外层则是针对这个内层查询的结果集进行查询通过where条件来进行过滤。
子查询里的那个语句的执行计划,select_type是derived,意思是针对子查询执行后的结果集会物化为一个内部临时表,然后外层查询是针对这个临时的物化表执行的。
5、type取值
select * fromt1 where id=110
EXPLAIN select * from t1 inner join t2 on t1.id = t2.id
![](/assets/blank.gif)
- 针对t1表是一个全表扫描,这个是必然的,因为关联的时候会先查询一个驱动表,这里就是t1,他没什么where筛选条件,自然只能是全表扫描查出来所有的数据了。
- 针对t2表的查询type是eq_ref,而且使用了PRIMARY主键。针对t1表全表扫描获取到的每条数据,都会去t2表里基于主键进行等值匹配,此时会在t2表的聚簇索引里根据主键值进行快速查找,所以在连接查询时,针对被驱动表如果基于主键进行等值匹配,那么他的查询方式就是eq_ref了。
- 如果正常基于某个二级索引进行等值匹配,type就会是ref。
- 如果基于二级索引查询的时候允许值为null,那么查询方式就会是ref_or_null。
- 针对单表查询可能会基于多个索引提取数据后进行合并,此时查询方式是index_merge。
- range的话就是基于二级索引进行范围查询。
- 查询方式是index的时候是直接扫描二级索引的叶子节点,也就是扫描二级索引里的每条数据
- all的话就是全表扫描,也就是对聚簇索引的叶子节点扫描每条数据。
6、possible_keys/ref取值
- possible_keys:就是在针对一个表进行查询的时候有哪些潜在可以使用的索引。
- 比如你有两个索引,一个是KEY(x1, x2, x3),一个是KEY(x1, x2, x4),此时要是在where条件里要根据x1和x2两个字段进行查询,那么此时明显是上述两个索引都可以使用的,那么到底要使用哪个呢?
- 此时就需要通过我们之前讲解的成本优化方法,去估算使用两个索引进行查询的成本,看使用哪个索引 的成本更低,那么就选择用那个索引,最终选择的索引,就是执行计划里的key这个字段的值了。
- key_len:就是当你在key里选择使用某个索引之后,那个索引里的最大值的长度是多少,这个就是给你一个参考,大概知道那个索引里的值最大能有多长。
EXPLAIN SELECT * FROM t1 WHERE x1 = 'xxx'
针对t1表的查询,type是ref方式的,也就是说基于普通的二级索引进行等值匹配,然后possible_keys只有一个就是index_x1,针对x1字段建立的一个索引,而实际使用的索引也是index_x1,毕竟就他一个是可以用的。
EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id;
针对t1表作为驱动表执行一个全表扫描,接着针对t1表里每条数据都会去t2表根据t2表的主键执行等值匹配,所以第二个执行计划的type是eq_ref,意思就是被驱动表基于主键进行等值匹配,而且使用的索引是PRIMARY,就是使用了t2表的主键。
- ref:到底是谁跟t2表的聚簇索引里的主键值进行等值匹配呢?是常量值吗?不是,是test_db这个库下的t1表的id字段,这里跟t2表的主键进行 等值匹配的是t1表的主键id字段。
- rows:就是说你使用指定的查询方式,会查出来多少条数据,
- filtered:在查询方式查出来的这波数据里,再用上其他的不在索引范围里的查询条件,又会过滤出来百分之几的数据。
EXPLAIN SELECT * FROM t1 WHERE x1 > 'xxx' AND x2 = 'xxx'
针对t1表的查询方式是range,也就是基于索引进行范围查询,用的索引是index_x1,也就是x1字段的索引,然后基于x1>'xxx'这个条件通过index_x1索引查询出来的数据大概是1987条,接着会针对这1987条数据再基于where条件里的其他条件,也就是x2='xxx'进行过滤。
7、extra取值
EXPLAIN SELECT x1 FROM t1 WHERE x1 = 'xxx'
首先他是访问了t1表,使用的是ref访问方法,也就是基于二级索引去查找,找的是index_x1这个索引,这个索引的最大数据长度是456字节,查找的目标是一个const代表的常量值,通过索引可以查出来25条数据,经过其他条件筛选过后,最终剩下数据是100%。
extra的信息:Using index是说这次查询,仅仅涉及到了一个二级索引,不需要回表,因为他仅仅是查出来了x1这个字段,直接从index_x1索引里查就行了。如果没有回表操作,仅仅在二级索引里执行,那么extra里会告诉是Using index。
SELECT * FROM t1 WHERE x1 > 'xxx' AND x1 LIKE '%xxx'
8、Using where/join buffer
EXPLAIN SELECT * FROM t1 WHERE x2 = 'xxx'
EXPLAIN SELECT * FROM t1 WHERE x1 = 'xxx' AND x2 = 'xxx'
![](/assets/blank.gif)
针对t1表去查询,先通过ref方式直接在index_x1索引里查找,是跟const代表的常量值去查找,然后查出来250条数据,接着再用Using where代表的方式,去使用AND x2 = 'xxx'条件进行筛选,筛选后的数据比例是18%,最终所以查出来的数据大概应该是45条。
EXPLAIN select * from t1 inner join t2 on t1.x2 = t2.x2
![](/assets/blank.gif)
因为要执行join,那么肯定是先得查询t1表的数据,此时是对t1表直接全表查询,查出来4578条数据,接着似乎很明确了,就是对每条数据的x2字段的值,跑到t2表里去查对应的数据,进行关联。 但是此时因为 t2 表也没法根据索引来查,也是属于全表扫描,所以每次都得对t2表全表扫描一下,根据extra提示的Using where,就是根据t1表每条数据的x2字段的值去t2表查找对应的数据了,然后此时会用join buffer技术,在内存里做一些特殊优化,减少t2表的全表扫描次数。
9、Using filesore/Using tempory
EXPLAIN SELECT * FROM t1 ORDER BY x1 LIMIT 10
![](/assets/blank.gif)
EXPLAIN SELECT * FROM t1 ORDER BY x2 LIMIT 10
他基于x2字段来排序,是没法直接根据有序的索引去找数据的,只能把所有数据写入一个临时的磁盘文件,基于排序算法在磁盘文件里按照x2字段的值完成排序,然后再按照LIMIT 10的要求取出来头10条数据。 把表全数据放磁盘文件排序的做法真的是相当的糟糕,性能其实会极差的。
EXPLAIN SELECT x2, COUNT(*) AS amount FROM t1 GROUP BY x2
![](/assets/blank.gif)
Mysql基础(十六):explain命令SQL执行计划相关推荐
- 闲聊MySQL(九):浅析SQL执行计划
文章目录 前言 SQL执行计划 Explain输出字段 id select_type table partitions type possible_keys key key_len ref rows ...
- ClickHouse 使用EXPLAIN 分析 SQL 执行计划
1.1. 使用 EXPLAIN 分析 SQL 执行计划 本节介绍如何使用EXPLAIN命令分析SQL语句的执行计划. 1.1.1. EXPLAIN概述 执行计划是进行SQL查询调优的重要参考.在Cli ...
- 【ClickHouse SQL 极简教程】使用EXPLAIN 分析 SQL 执行计划
1.1. 使用 EXPLAIN 分析 SQL 执行计划 本节介绍如何使用EXPLAIN命令分析SQL语句的执行计划. 1.1.1. EXPLAIN概述 执行计划是进行SQL查询调优的重要参考.在Cli ...
- mysql 执行计划extra_SQL优化 MySQL版 -分析explain SQL执行计划与Extra
Extra 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 注:此文章必须有一定的Mysql基础,或观看执行计划入门篇传送门: https://www.cnblogs.com/Sta ...
- mysql 笛卡尔积影响_SQL优化 MySQL版 -分析explain SQL执行计划与笛卡尔积
SQL优化 MySQL版 -分析explain SQL执行计划 作者 Stanley 罗昊 [转载请注明出处和署名,谢谢!] 首先我们先创建一个数据库,数据库中分别写三张表来存储数据; course: ...
- mysql生成树状执行计划_SQL优化 MySQL版 -分析explain SQL执行计划与笛卡尔积
SQL优化 MySQL版 -分析explain SQL执行计划 作者 Stanley 罗昊 [转载请注明出处和署名,谢谢!] 首先我们先创建一个数据库,数据库中分别写三张表来存储数据; course: ...
- 【SQL开发实战技巧】系列(六):从执行计划看NOT IN、NOT EXISTS 和 LEFT JOIN效率,记住内外关联条件不要乱放
系列文章目录 [SQL开发实战技巧]系列(一):关于SQL不得不说的那些事 [SQL开发实战技巧]系列(二):简单单表查询 [SQL开发实战技巧]系列(三):SQL排序的那些事 [SQL开发实战技巧] ...
- 来来来!一次搞定各种数据库 SQL 执行计划:MySQL、Oracle
执行计划(execution plan,也叫查询计划或者解释计划)是数据库执行 SQL 语句的具体步骤,例如通过索引还是全表扫描访问表中的数据,连接查询的实现方式和连接的顺序等.如果 SQL 语句性能 ...
- MySQL(三)——SQL执行计划分析
目录 一.SQL执行计划概述 二.explain字段详解 1.id 2.select_type 3.possible_keys 4.key 5. key_len 6.ref 7.rows 8.Extr ...
最新文章
- 尹伊:用敏捷开发思维来成长!
- 想学习编程,我是如何入坑python的?
- python入门到精通需要学多久-python学习从入门到精通要多久
- LeetCode Range Sum Query - Mutable(树状数组、线段树)
- python卸载模块的方法汇总_Python卸载模块的方法汇总
- linux(ubuntu)~终端(terminal)shell操作指令
- javaweb应用开发与实践pdf_基于阿里云打造「云原生」Web应用——「懒猪行」Web应用开发实践...
- 安装redis提示[test] error 2_技术干货分享:一次flask+redis的微服务实战
- Centos 7初始化脚本
- HMM一文搞懂HMM(隐马尔可夫模型)
- Time flies
- 易语言精益模块json_精易模块|精易模块下载 v3.46 官方免费版_最火软件站
- 米家插件平台的技术实践之路
- wstmart不错。可以用用
- C语言期末考试成绩奖励编码,C语言期末考试总结,看完保你过
- 数据结构与算法10:图与图搜索
- Android7.08.0 电池图标分析
- Chatgpt的三个镜像网站(无需”魔法“直接用)
- JAVA毕业设计家乡旅游文化推广网站计算机源码+lw文档+系统+调试部署+数据库
- 绘制帧率与实际显示时帧率不同步,怎么测得显示帧率?