sparksql 中外连接查询中的谓词下推处理

上月听了本部门sparksql大牛的sparksql调优分享,当时对一个点不是很理解,回去好好理了一下,整理成文。

1. 谓词下推

基本概念:谓词下推(predicate pushdown)属于逻辑优化。优化器可以将谓词过滤下推到数据源,从而使物理执行跳过无关数据。在使用Parquet或者orcfile的情况下,更可能存在文件被整块跳过的情况,同时系统还通过字典编码把字符串对比转换为开销更小的整数对比。

说白了,就是把查询相关的条件下推到数据源进行提前的过滤操作,之所以这里说是查询相关的条件,而不直接说是where 后的条件,是因为sql语句中除了where后的有条件外,join时也有条件。本文讨论的主要就是join时的条件的处理。

2. join中条件和join后条件

那么这两类不同的条件,在外连接查询中是否都会下推呢?不是的,是否下推是遵循一定规则的,对于左连接查询,可以归纳为下表:

左表

右表

Join中条件

不下推

下推

Join后条件

下推

不下推

3. 具体情况具体分析

帽子很高,其实就是对2中表格中的规则一个一个来分析。

3.1. 左表join后条件下推

  • 查询语句如下:
  • 表结构和数据如下:

左表:

Id

value

1

one

2

two

右表:

id

value

1

one

2

two

来分析一下LT.id>1下推到左表左表进行数据过滤的结果:

经过LT.id>1过滤后,左表变为:

Id

value

2

two

此时再和右表进行左连接,流程如下:

左表id为2的行,在右表中能join上,则连接结果如下:

LT.id

LT.value

RT.value

2

two

two

可见,条件下推过滤了左表整整50%的数据,相当牛叉,虽然只有两条。究其原因,是因为在sparksql中,把以上的查询解析成了如下的子查询:

3.2. 左表join中条件不下推

查询语句如下:

左表:

Id

value

1

one

2

two

右表:

id

value

1

one

2

two

来看看不下推的情况下计算出的正确结果,join过程如下:

第一步:左表id为1的行在右表中能找到相等的id,但是左表的id为1,是不满足第二个join条件的,所以左表这一条相当于没有和右表join上,所以左表的值value保留,而右表的value为null。

第二步:左表id为2 的行在右表中能找到,而且左表id为2的行的id大于1,两个join条件都满足,所以算是和右表join上了,所以左表和右表的value都保留。

LT.id

LT.value

RT.value

1

one

null

2

two

two

3.3. 右表join中条件下推

查询语句如下:

现在把RT.id>1这个右表join中条件下推,来过滤右表,过滤后如下:

Id

value

2

two

然后左表再和右表进行左连接,流程如下:

第一步:左表id为1的行在右表中没有,此时左表值保留,右表为null

第二步:左表id位2的行在右表中有,并且RT.id大于1,两个join条件都满足,则左表和右表的值都保留。

LT.id

LT.value

RT.value

1

one

null

2

two

two

那么如果不下推,来看看结果,流程如下:

第一步:左表id为1的行在右表中有,但是不满足第二个join条件,所以这行算是没join上,所以左表数据保留,右表为null

第二步:左表id为2的行在右表中有,也满足第二个join条件,所以左右表的数据都保留。

LT.id

LT.value

RT.value

1

one

null

2

wo

two

可见,右表join中条件下推不下推,结果一样,所以,干吗不下推?可以过滤掉一半的数据呢。Sparksql中的等价处理语句是:

3.4. 右表join后条件不下推

这个应该是最违反常规理解的查询了,查询语句如下:

首先来看,join后条件不下推的情况,流程如下:

第一步:左表id为1的行在右表中可以找到,但是此时仅仅满足join条件,在使用where条件判断这条连接后数据时,发现右表的id不满足RT.id>1的条件,所以这条join结果不保留(注意,这里是不保留,全都不保留,左表右表都不保留,要跟上边的没join上,右表的值为null的情况区别开,这也是关键所在)

第二步:左表id为2的行和右表id为2的行join上了,同时也满足RT.id>1的where条件。

LT.id

LT.value

RT.value

2

two

two

很明显,这是一条符合语义的正确的查询结果。

好了,接下来看看右表join后条件下推的情况:

第一步:使用RT.id>1过滤右表,过滤后右表只剩一行id为2的行

第二步:左表id为1的行在右表中没有,此时左表值保留,右表值为null

第三步:左表id为2的行在右表中有,此时左表值保留,右表值也保留。

结果如下:

LT.id

LT.value

RT.value

1

one

null

2

two

two

这其实是一个错误的结果。

好了分析结束,其实大家也看出来了,我是打着sparksql的幌子骗点击的,任何数据库其实都会按照这个规则处理的,不是sparksql所特有的。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

兵马勇的专栏

1 篇文章1 人订阅

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

MySQL/MariaDB基础性知识及DDL操作详解

前言 MySQL/MariaDB是一个开放源码的小型关联式数据库管理系统,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降...

4266
来自专栏后端技术探索

MySQL中的两种临时表 外部临时表

通过CREATE TEMPORARY TABLE 创建的临时表,这种临时表称为外部临时表。这种临时表只对当前用户可见,当前会话结束的时候,该临时表会自动关闭。这...

720
来自专栏运维小白

13.5 常用sql语句

常用sql语句目录概要 select count(*) from mysql.user; select * from mysql.db; select db f...

2275
来自专栏一英里广度一英寸深度的学习

SQL 内连接,外连接(左外连接、右外连接)

语句:select * from a_table a inner join b_table bon a.a_id = b.b_id;

5992
来自专栏Linyb极客之路

SQL优化指南

slow_launch_time:表示如果建立线程花费了比这个值更长的时间,slow_launch_threads 计数器将增加

912
来自专栏MYSQL轻松学

MySQL replace用法简介

今天在工作的过程中碰到一个问题,要把数据库中某个列的所有值中含有"ceshi.test.com"的字符去掉,本来可以写个脚本,把所有的值都取出再导入进行处理,但...

3769
来自专栏乐沙弥的世界

MySQL数据类型选择性能比对

版权声明:本文为博主原创文章,欢迎扩散,扩散请务必注明出处。 https://blog.csdn.net/robinson_0612/art...

822
来自专栏北京马哥教育

给linux用户的11个高级MySQL数据库面试问题和答案

1. 如何使用SELECT语句找到你正在运行的服务器的版本并打印出当前数据库的名称? 答:下面的语句的结果会显示服务器的版本和当前的数据库名称 mysql>...

2674
来自专栏后端技术探索

一次非常有意思的sql优化经历

发现没有用到索引,type全是ALL,那么首先想到的就是建立一个索引,建立索引的字段当然是在where条件的字段。

761
来自专栏后端之路

坑爹的MySql update in subquery

背景 ? 最近收到邮件如上,在做allot时发生db超时【前一段时间由于系统负载过大忽略了相关慢sql】 收到如上邮件考虑可能是allot详情太多导致发生超时 ...

2389

扫码关注云+社区