EXPLAIN TYPE 列的 JOIN 常见场景详解(上)
专栏连载至此,相信读者们已经对一条 SQL 的优化步骤、执行计划等有了一个大概的了解。那接下来我们对 MySQL 的执行计划输出进行详细解释,以便大家对其了解的更加深入。
>作者:杨涛涛,爱可生技术专家。 > >爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
我们这个标题为什么叫做EXPLAIN TYPE 列的JOIN 常见场景详解呢?从MySQL 优化器的角度来看,所有SQL都是JOIN查询(单表检索可以看成过滤字段和主键做JOIN的特殊类型)。由于内容太多,我分成了上下两部分,今天我们来从第一部分开始。
还是表t1,不过我对表结构做了少许变更,更改原来的自增主键为联合主键(f0,f1),表记录数不变,还是10W行。
CREATE TABLE `t1` ( `f0` int NOT NULL, `f1` int NOT NULL, `r1` int DEFAULT NULL, `r2` int DEFAULT NULL, `r3` int DEFAULT NULL, `log_date` date DEFAULT NULL, PRIMARY KEY (`f0`,`f1`) ) ENGINE=InnoDB
接下来,我写了几条简单的SQL,来分别讲讲type列的意义.
第一,type 栏为"const"
这表明排除索引性能的话,这条SQL 一定是最优的。比如 SQL 1: 过滤字段为联合主键,并且是两个固定的常量比对,这种一定是最优化的:
SQL 1: select * from t1 where f0=110 and f1 = 778
执行计划如下:type 栏里是“const", ref 栏里是const,const。表明扫描表t1,给定两个常量来过滤,同时走的索引是主键,可以联合rows栏一起看,如果type栏相同,那么rows栏数值小的肯定较为优化。
debian-ytt1:ytt>desc select * from t1 where f0=110 and f1 = 778\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: const possible_keys: PRIMARY key: PRIMARY key_len: 8 ref: const,const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
第二,type 栏为"eq_ref"
这其实和const类似,也是优化比率靠前的,不同的是eq_ref用于两张真实的表JOIN,并且两表的JOIN KEY 必须为主键(或者唯一索引)的全部,同时对于被驱动表而言,对它进行检索的过滤条件是驱动表的所有主键,每次只有一行。(关于JOIN 的优化我会另外开篇细讲,这里就不多说了)
比如SQL 2:select * from t1 join t2 using(f0,f1)
SQL 2 是两表做内联,并且联接的键为两表的主键,这样的SQL 语句(仅从SQL 语句角度,不掺杂业务逻辑)是两表联接类型里不带过滤条件的场景下最优的。
那依然看下执行计划: 这里省去表t2的执行计划,只看表t1。 对于表t1来讲,对它的扫描基于主键,并且在扫描主键时,每次给的常量值为表t2的联合主键,而且是非常精确的一行。
debian-ytt1:ytt>desc select * from t1 join t2 using(f0,f1)\G ... *************************** 2. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 8 ref: ytt.t2.f0,ytt.t2.f1 rows: 1 filtered: 100.00 Extra: NULL 2 rows in set, 1 warning (0.00 sec)
第三,type 栏为"ref"
ref 和eq_ref 类似,不同的是两表的JOIN KEY 非主键、非唯一索引。 这种场景从SQL角度来讲,应该避免掉;如果实在无法避免,可以想办法减少两表JOIN的记录数。
那对SQL2 做些调整,变为SQL 3: JOIN 条件变为字段r1,并且同时给两表字段r1加索引。
SQL 3: select * from t1 a join t2 b using(r1)
再看下查询计划:还是省去表t1,只看表t2的执行计划。 这里对表t2的检索走索引idx_r1,同时每次扫描引用表t1字段r1,可以结合rows栏来看,这条SQL其实并不优化。
debian-ytt1:ytt>desc select * from t1 a join t2 b using(r1)\G ... *************************** 2. row *************************** id: 1 select_type: SIMPLE table: b partitions: NULL type: ref possible_keys: idx_r1 key: idx_r1 key_len: 5 ref: ytt.a.r1 rows: 19838 filtered: 100.00 Extra: NULL 2 rows in set, 1 warning (0.01 sec)
第四,type 栏为"range"
range 代表范围扫描,和前面三个不同,前面三个都是基于常量。
来看下SQL 4: select * from t1 where f0<120
SQL 4 对表t1的检索条件是一个范围(-INF,120),执行计划如下: 对表t1的扫描走主键,类型为range。
debian-ytt1:ytt>desc select * from t1 where f0<120\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: range possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: NULL rows: 93 filtered: 100.00 Extra: Using where 1 row in set, 1 warning (0.00 sec)
SQL 4 是对表t1的范围扫描,有些时候基于一些表记录特殊性(不具备通用性),可以把范围扫描优化为常量扫描。 这里表t1就具有特殊性,对于字段过滤条件为f0<120的结果和过滤条件为f0=110的结果是一样的,所以改SQL 4 为 SQL 5:
SQL 5: select * from t1 where f0=110
看下SQL 5的执行计划: 成功把对表t1的范围扫描变为常量扫描,type 栏由range 变为ref.
debian-ytt1:ytt>desc select * from t1 where f0=110\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 93 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
其实这点从传统的执行计划结果里看不出什么效果,还是得实际执行后,看两条SQL 的执行成本。 我们使用explain analyze 来对比下SQL 4和 SQL 5的执行成本: SQL 4 成本为18.93, SQL 5成本为 9.62, 性能提升很明显。
debian-ytt1:ytt>desc analyze select * from t1 where f0< 120\G *************************** 1. row *************************** EXPLAIN: -> Filter: (t1.f0 < 120) (cost=18.93 rows=93) (actual time=0.040..0.061 rows=93 loops=1) -> Index range scan on t1 using PRIMARY (cost=18.93 rows=93) (actual time=0.038..0.047 rows=93 loops=1) 1 row in set (0.00 sec) debian-ytt1:ytt>desc analyze select * from t1 where f0=110\G *************************** 1. row *************************** EXPLAIN: -> Index lookup on t1 using PRIMARY (f0=110) (cost=9.62 rows=93) (actual time=0.065..0.087 rows=93 loops=1) 1 row in set (0.00 sec)
第五,type 栏为"index"
Index 表示覆盖索引扫描,可以简单描述为没有过滤条件的索引扫描;更进一步,如果从索引角度来讲,就是全表扫了。
比如SQL 6:select r1 from t1 limit 10
SQL 6 扫描的列只有r1,而非全部字段,此刻走索引idx_r1即可,不需要回表。
执行计划如下:type 为Index, 使用索引idx_r1, 扫描行数为10W行,刚好表t1总记录数也是10W.
debian-ytt1:ytt>desc select r1 from t1 limit 10 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: index possible_keys: NULL key: idx_r1 key_len: 5 ref: NULL rows: 106313 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec)
其实对于SQL 6 来讲,有limit 10 子句是可以提前终止扫描的,但是这里MySQL为什么还是扫描所有行? 这里MySQL虽然走了索引idx_r1, 但是没有排序子句,进而造成MySQL 不知道按照什么顺序输出,只能扫描所有记录。
对于这类的优化,可以加一个排序子句,把现有索引的预排序特性利用上,变为 SQL 7:
SQL 7: select r1 from t1 order by r1 limit 10;
此时再查看查询计划:很显然,MySQL根据利用索引idx_r1的有序性,加上limit 子句,提前终止了扫描。
debian-ytt1:ytt>explain select r1 from t1 order by r1 limit 10\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: index possible_keys: NULL key: idx_r1 key_len: 5 ref: NULL rows: 10 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec)
关于EXPLAIN TYPE 栏 的JOIN常见场景上篇就到这里了,欢迎大家订阅下一篇。
更多技术文章,请访问:https://opensource.actionsky.com/
关于 SQLE
SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。
✨ Github:https://github.com/actiontech/sqle
📚 文档:https://actiontech.github.io/sqle-docs/
💻 官网:https://opensource.actionsky.com/sqle/
👥 微信群:请添加小助手加入 ActionOpenSource

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
开源协议专题二:GPL协议的法律解析(上)
一、引言 严格开源协议具体可以分为强传染性和弱传染性两类,前者包括GPL(GNU通用公共许可证)等,后者则包括MPL(Mozilla公共许可证)等。二者的区别在于:强传染性协议(GPL)要求任何基于该协议发布的软件及其衍生作品都必须以相同的许可条款发布,确保整个项目的源代码开放;而弱传染性协议(MPL)则允许部分代码在特定条件下保持闭源,但仍然要求某些核心部分或修改的部分保持开源。 GPL协议共有三个版本,本文将以《GNU通用公共许可证第三版》(GNU General Public License version 3,以下简称GPL v3)为例,分为上下两篇详细探讨其主要内容和法律性质。上篇将重点介绍GPL v3的主要条款,旨在提供一个全面的理解框架。鉴于传染性条款的重要性及其对开源社区的影响,下篇将专门探讨这一关键条款,深入分析其法律含义和应用。 二、概述 GPL v3是自由软件基金会(Free Software Foundation, FSF)于2007年发布的开源软件许可协议,旨在确保软件的自由使用、修改和分发。它与前两版的内容对比如下: 对比项目 GPL v1(1989年发布)...
- 下一篇
Elasticsearch:什么是信息检索?
信息检索定义 信息检索 (IR) 是一种有助于从大量非结构化或半结构化数据中有效、高效地检索相关信息的过程。信息(IR)检索系统有助于搜索、定位和呈现与用户的搜索查询或信息需求相匹配的信息。 作为信息访问的主要形式,信息检索是每天使用搜索引擎的数十亿人的依靠。信息检索系统部署了各种模型、算法和日益先进的技术(例如:向量搜索),使搜索访问范围广泛且不断增长的来源成为可能,包括文档、文档中的项目、元数据以及文本、图像、视频和声音的数据库。 信息检索简史 信息检索的起源可以追溯到古代,当时人们建立了图书馆和档案馆来组织和存储信息,包括对学术作品进行索引和字母排序。到了 19 世纪,人们开始使用打孔卡来处理信息,1931 年,伊曼纽尔·戈德堡 (Emanuel Goldberg) 获得了第一台成功的机电文档检索设备的专利,该设备被称为 “统计机(Statistical Machine)”,旨在搜索胶片上编码的数据。 随着现代计算机的发展,信息检索在 20 世纪中叶开始正式化为一门科学学科。杰拉德·萨尔顿 (Gerard Salton) 和汉斯·彼得·卢恩 (Hans Peter Luhn) 开...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Red5直播服务器,属于Java语言的直播服务器
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2配置默认Tomcat设置,开启更多高级功能