百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

「大数据」SparkSql连接查询中的谓词下推处理(一)

liebian365 2024-10-28 17:17 31 浏览 0 评论

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/YPN85WBNcnhk8xKjTPTa2g

作者:李勇

目录:

1.SparkSql

2.连接查询和连接条件

3.谓词下推

4.内连接查询中的谓词下推规则

4.1.Join后条件通过AND连接

4.2.Join后条件通过OR连接

4.3.分区表使用OR连接过滤条件

1.SparkSql

SparkSql 是架构在 Spark 计算框架之上的分布式 Sql 引擎,使用 DataFrame 和 DataSet 承载结构化和半结构化数据来实现数据复杂查询处理,提供的 DSL可以直接使用 scala 语言完成 Sql 查询,同时也使用 thriftserver 提供服务化的 Sql 查询功能。

SparkSql 提供了 DataSource API ,用户通过这套 API 可以自己开发一套 Connector,直接查询各类数据源,数据源包括 NoSql、RDBMS、搜索引擎以及 HDFS 等分布式文件系统上的文件等。和 SparkSql 类似的系统有 Hive、PrestoDB 以及 Impala,这类系统都属于所谓的" Sql on Hadoop "系统,每个都相当火爆,毕竟在这个不搞 SQL 就是耍流氓的年代,没 SQL 确实很难找到用户使用。

2.连接查询和连接条件

Sql中的连接查询(join),主要分为内连接查询(inner join)、外连接查询(outter join)和半连接查询(semi join),具体的区别可以参考wiki的解释。

连接条件(join condition),则是指当这个条件满足时两表的两行数据才能"join"在一起被返回,例如有如下查询:

其中的"LT.id=RT.idAND LT.id>1"这部分条件被称为"join中条件",直接用来判断被join的两表的两行记录能否被join在一起,如果不满足这个条件,两表的这两行记录并非全部被踢出局,而是根据连接查询类型的不同有不同的处理,所以这并非一个单表的过滤过程或者两个表的的“联合过滤”过程;而where后的"RT.id>2"这部分被称为"join后条件",这里虽然成为"join后条件",但是并非一定要在join后才能去过滤数据,只是说明如果在join后进行过滤,肯定可以得到一个正确的结果,这也是我们后边分析问题时得到正确结果的基准方法。

3.谓词下推

所谓谓词(predicate),英文定义是这样的:A predicate is a function that returns bool (or something that can be implicitly converted to bool),也就是返回值是true或者false的函数,使用过scala或者spark的同学都知道有个filter方法,这个高阶函数传入的参数就是一个返回true或者false的函数。

但是如果是在sql语言中,没有方法,只有表达式。where后边的表达式起的作用正是过滤的作用,而这部分语句被sql层解析处理后,在数据库内部正是以谓词的形式呈现的。

那么问题来了,谓词为什么要下推呢? SparkSql中的谓词下推有两层含义,第一层含义是指由谁来完成数据过滤,第二层含义是指何时完成数据过滤。要解答这两个问题我们需要了解SparkSql的Sql语句处理逻辑,大致可以把SparkSql中的查询处理流程做如下的划分:

SparkSql首先会对输入的Sql语句进行一系列的分析(Analyse),包括词法解析(可以理解为搜索引擎中的分词这个过程)、语法分析以及语义分析(例如判断database或者table是否存在、group by必须和聚合函数结合等规则);之后是执行计划的生成,包括逻辑计划和物理计划。其中在逻辑计划阶段会有很多的优化,对谓词的处理就在这个阶段完成;而物理计划则是RDD的DAG图的生成过程;这两步完成之后则是具体的执行了(也就是各种重量级的计算逻辑,例如join、groupby、filter以及distinct等),这就会有各种物理操作符(RDD的Transformation)的乱入。

能够完成数据过滤的主体有两个,第一是分布式Sql层(在execute阶段),第二个是数据源。那么谓词下推的第一层含义就是指由Sql层的Filter操作符来完成过滤,还是由Scan操作符在扫描阶段完成过滤。

上边提到,我们可以通过封装SparkSql的Data Source API完成各类数据源的查询,那么如果底层数据源无法高效完成数据的过滤,就会执行全局扫描,把每条相关的数据都交给SparkSql的Filter操作符完成过滤,虽然SparkSql使用的Code Generation技术极大的提高了数据过滤的效率,但是这个过程无法避免大量数据的磁盘读取,甚至在某些情况下会涉及网络IO(例如数据非本地化存储时);如果底层数据源在进行扫描时能非常快速的完成数据的过滤,那么就会把过滤交给底层数据源来完成(至于哪些数据源能高效完成数据的过滤以及SparkSql又是如何完成高效数据过滤的则不是本文讨论的重点,会在其他系列的文章中介绍)。

那么谓词下推第二层含义,即何时完成数据过滤则一般是在指连接查询中,是先对单表数据进行过滤再和其他表连接还是在先把多表进行连接再对连接后的临时表进行过滤,则是本系列文章要分析和讨论的重点。

4.内连接查询中的谓词下推规则

假设我们有两张表,表结构很简单,数据也都只有两条,但是足以讲清楚我们的下推规则,两表如下,一个lefttable,一个righttable:

4.1.Join后条件通过AND连接

先来看一条查询语句:

这个查询是一个内连接查询,join后条件是用and连接的两个表的过滤条件,假设我们不下推,而是先做内连接判断,这时是可以得到正确结果的,步骤如下:

  1. 左表id为1的行在右表中可以找到,即这两行数据可以"join"在一起
  2. 左表id为2的行在右表中可以找到,这两行也可以"join"在一起

至此,join的临时结果表(之所以是临时表,因为还没有进行过滤)如下:

然后使用where条件进行过滤,显然临时表中的第一行不满足条件,被过滤掉,最后结果如下:

来看看先进行谓词下推的情况。先对两表进行过滤,过滤的结果分别如下:

然后再对这两个过滤后的表进行内连接处理,结果如下:

可见,这和先进行join再过滤得到的结果一致。

4.2.Join后条件通过OR连接

再来看一条查询语句:

我们先进行join处理,临时表的结果如下:

然后使用where条件进行过滤,最终查询结果如下:

如果我们先使用where条件后每个表各自的过滤条件进行过滤,那么两表的过滤结果如下:

然后对这两个临时表进行内连接处理,结果如下:

表格有问题吧,只有字段名,没有字段值,怎么回事?是的,你没看错,确实没有值,因为左表过滤结果只有id为1的行,右表过滤结果只有id为2的行,这两行是不能内连接上的,所以没有结果。

那么为什么where条件中两表的条件被or连接就会出现错误的查询结果呢?分析原因主要是因为,对于or两侧的过滤条件,任何一个满足条件即可以返回TRUE,那么对于"LT.value = 'two' OR RT.value = 'two' "这个查询条件,如果使用LT.value='two'把只有LT.value为'two'的左表记录过滤出来,那么对于左表中LT.value不为two的行,他们可能在跟右表使用id字段连接上之后,右表的RT.value恰好为two,也满足"LT.value = 'two' OR RT.value = 'two' ",但是可惜呀可惜,这行记录因为之前的粗暴处理,已经被过滤掉,结果就是得到了错误的查询结果。所以这种情况下谓词是不能下推的。

但是OR连接两表join后条件也有两个例外,这里顺便分析第一个例外。第一个例外是过滤条件字段恰好为Join字段,比如如下的查询:

在这个查询中,join后条件依然是使用OR连接两表的过滤条件,不同的是,join中条件不再是id相等,而是value字段相等,也就是说过滤条件字段恰好就是join条件字段。大家可以自行采用上边的分步法分析谓词下推和不下推时的查询结果,得到的结果是相同的。

我们来看看上边不能下推时出现的情况在这种查询里会不会出现。对于左表,如果使用LT.value='two'过滤掉不符合条件的其他行,那么因为join条件字段也是value字段,说明在左表中LT.value不等于two的行,在右表中也不能等于two,否则就不满足"LT.value=RT.value"了。这里其实有一个条件传递的过程,通过join中条件,已经在逻辑上提前把两表整合成了一张表。

至于第二个例外,则涉及了SparkSql中的一个优化,所以需要单独介绍。

4.3.分区表使用OR连接过滤条件

如果两个表都是分区表,会出现什么情况呢?我们先来看如下的查询:

此时左表和右表都不再是普通的表,而是分区表,分区字段是pt,按照日期进行数据分区。同时两表查询条件依然使用OR进行连接。试想,如果不能提前对两表进行过滤,那么会有非常巨量的数据要首先进行连接处理,这个代价是非常大的。但是如果按照我们在2中的分析,使用OR连接两表的过滤条件,又不能随意的进行谓词下推,那要如何处理呢?SparkSql在这里使用了一种叫做“分区裁剪”的优化手段,即把分区并不看做普通的过滤条件,而是使用了“一刀切”的方法,把不符合查询分区条件的目录直接排除在待扫描的目录之外。

我们知道分区表在HDFS上是按照目录来存储一个分区的数据的,那么在进行分区裁剪时,直接把要扫描的HDFS目录通知Spark的Scan操作符,这样,Spark在进行扫描时,就可以直接咔嚓掉其他的分区数据了。但是,要完成这种优化,需要SparkSql的语义分析逻辑能够正确的分析出Sql语句所要表达的精确目的,所以分区字段在SparkSql的元数据中也是独立于其他普通字段,进行了单独的标示,就是为了方便语义分析逻辑能区别处理Sql语句中where条件里的这种特殊情况

相关推荐

“版本末期”了?下周平衡补丁!国服最强5套牌!上分首选

明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...

VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"

首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...

东营交警实名曝光一批酒驾人员名单 88人受处罚

齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...

Qt界面——搭配QCustomPlot(qt platform)

这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...

大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写

老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...

测试谷歌VS Code AI 编程插件 Gemini Code Assist

用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...

顾爷想知道第4.5期 国服便利性到底需优化啥?

前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...

掌握Visual Studio项目配置【基础篇】

1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...

还嫌LED驱动设计套路深?那就来看看这篇文章吧

随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...

Visual Studio Community 2022(VS2022)安装图文方法

直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...

Qt添加MSVC构建套件的方法(qt添加c++11)

前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...

Qt为什么站稳c++GUI的top1(qt c)

为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...

qt开发IDE应该选择VS还是qt creator

如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...

Qt 5.14.2超详细安装教程,不会来打我

Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...

Cygwin配置与使用(四)——VI字体和颜色的配置

简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...

取消回复欢迎 发表评论: