SparkSQL之Analyzed LogicalPlan生成过程

  经过AstBuilder的处理,得到了Unresolved LogicalPlan。该逻辑算子树中未被解析的有UnresolvedRelation和UnresolvedAttribute两种对象。Analyzer所起到的主要作用就是将这两种节点或表达式解析成有类型的(Typed)对象。在此过程中,需要用到Catalog的相关信息。
  因为继承自RuleExecutor类,所以Analyzer执行过程会调用其父类RuleExecutor中实现的run方法,主要的不同之处是Analyzer中重新定义了一系列规则,即RuleExecutor类中的成员变量batches,如下图所示。
请添加图片描述
  在Spark 2.1版本中,Analyzer默认定义了6个Batch,共有34条内置的规则外加额外实现的扩展规则(上图中extendedResolutionRules)。在分析Analyzed LogicalPlan生成过程之前,先对这些Batch进行简单的介绍,读者可结合代码阅读。

Note:Analyzer中用到的规则比较多,因篇幅所限不方便一一展开分析。本小节对这些规则仅做概述性的分析,从宏观层面介绍规则所起到的主要作用,旨在把握规则体系的轮廓,后续章节在具体的查询分析时会对其中常用的重要规则进行讲解。

(1)Batch Substitution
顾名思义,Substitution含义是替换,因此这个Batch对节点的作用类似于替换操作。目前在Substitution这个Batch中,定义了4条规则,分别是CTESubstitution、WindowsSubstitution、EliminateUnions和 SubstituteUnresolvedOrdinals。

  • CTESubstitution:CTE对应的是With语句,在SQL中主要用于子查询模块化,因此CTESubstitution规则也就是用来处理With语句的。在遍历逻辑算子树的过程中,当匹配到With(child,relations)节点时,将子LogicalPlan替换成解析后的CTE。由于CTE的存在,SparkSqlParser对SQL语句从左向右解析后会产生多个LogicalPlan。这条规则的作用是将多个LogicalPlan合并成一个LogicalPlan。
  • WindowsSubstitution:对当前的逻辑算子树进行查找,当匹配到WithWindowDefinition(windowDefinitions,child)表达式时,将其子节点中未解析的窗口函数表达式(Unresolved-WindowExpression)转换成窗口函数表达式(WindowExpression)。
  • EliminateUnions:在Union算子节点只有一个子节点时,Union操作实际上并没有起到作用,这种情况下需要消除该Union节点。该规则在遍历逻辑算子树过程中,匹配到Union(children)且children的数目只有1个时,将Union(children)替换为children.head节点。
  • SubstituteUnresolvedOrdinals:Spark从2.0版本开始,在“Order By”和“Group By”语句中开始支持用常数来表示列的下标。例如,假设某行数据包括A、B、C 3列,那么1对应A列,2对应B列,3对应C列;此时“Group By 1,2”等价于“Group By A,B”语句。而在2.0版本之前,这种写法会直接被当作常数而忽略。新版本中这种特性通过配置参数“spark.sql.orderByOrdinal”和“spark.sql.groupByOrdinal”进行设置,默认都为true,表示该特性开启。SubstituteUnresolvedOrdinals这条规则的作用就是根据这两个配置参数将下标替换成UnresolvedOrdinal表达式,以映射到对应的列。

(2)Batch Resolution
该Batch中包含了Analyzer中最多同时也最常用的解析规则,如下表所示。表中规则从上到下的顺序也是规则被RuleExecutor执行的顺序。
根据表可知,Resolution中加入了25条分析规则,以及一个extendedResolutionRules扩展规则列表用来支持Analyzer子类在扩展规则列表中添加新的分析规则。整体上来讲,表中的这些规则涉及了常见的数据源、数据类型、数据转换和处理操作等。根据规则名称很容易看出,这些规则都针对特定的算子节点,例如ResolveUpCast规则用于DataType向DataType的数据类型转换。考虑到后续具体查询分析中会涉及这些规则,因此这里不展开分析。

(3)Batch Nondeterministic⇒PullOutNondeterministic
该Batch中仅包含PullOutNondeterministic这一条规则,主要用来将LogicalPlan中非Project或非Filter算子的nondeterministic(不确定的)表达式提取出来,然后将这些表达式放在内层的Project算子中或最终的Project算子中。

(4)Batch UDF⇒HandleNullInputsForUDF
对于UDF这个规则,Batch主要用来对用户自定义函数进行一些特别的处理,该Batch在Spark2.1版本中仅有HandleNullInputsForUDF这一条规则。HandleNullInputsForUDF规则用来处理输入数据为Null的情形,其主要思想是从上至下进行表达式的遍历(transform ExpressionsUp),当匹配到ScalaUDF类型的表达式时,会创建If表达式来进行Null值的检查。
请添加图片描述
(5)Batch FixNullability⇒FixNullability
该Batch中仅包含FixNullability这一条规则,用来统一设定LogicalPlan中表达式的nullable属性。在DataFrame或Dataset等编程接口中,用户代码对于某些列(AttribtueReference)可能会改变其nullability属性,导致后续的判断逻辑(如isNull过滤等)中出现异常结果。在FixNullability规则中,对解析后的LogicalPlan执行transform Expressions操作,如果某列来自于其子节点,则其nullability值根据子节点对应的输出信息进行设置。
(6)Batch Cleanup⇒CleanupAliases
该Batch中仅包含CleanupAliases这一条规则,用来删除LogicalPlan中无用的别名信息。一般情况下,逻辑算子树中仅Project、Aggregate或Window算子的最高一层表达式(分别对应project list、aggregate expressions和window expressions)才需要别名。CleanupAliases通过trimAliases方法对表达式执行中的别名进行删除。
  以上内容介绍的是Spark 2.1版本Analyzer中内置的分析规则整体情况,在不同版本的演化中,这些规则也会有所变化,读者可自行分析。现在回到之前案例查询中生成的Unresolved LogicalPlan中。接下来的内容将会重点探讨Analyzer对该逻辑算子树进行分析的详细流程。
  在QueryExecution类中可以看到,触发Analyzer执行的是execute方法,即RuleExecutor中的execute方法,该方法会循环地调用规则对逻辑算子树进行分析。

val analyzed: LogicalPlan = analyzer.execute(logical)

请添加图片描述
  对于上图中的Unresolved LogicalPlan,Analyzer中首先匹配的是ResolveRelations规则。执行过程如下图所示,这也是Analyzed LogicalPlan生成的第1步。
请添加图片描述

object ResolveRelations extends Rule[LogicalPlan] {private def lookupTableFromCatalog(u: UnresolvedRelation): LogicalPlan = {try {catalog.lookupRelation(u.tableIdentifier, u.alias)} catch {case _: NoSuchTableException => u.failAnalysis(s "Table or view not found: ${u.tableName}")}}def apply(paln: LogicalPlan): LogicalPlan = plan resolveOperators {case i @ InsertIntoTable(u: UnresolvedRelation, parts, child, _, _)if child.resolved => i.copy(table = EliminateSubqueryAliases(lookupTableFromCatelog(u)))case u: UnresolvedRelation => val table = u.tableIdentifierif(table.database.isDefined && conf.runSQLonFile && !catalog.isTemporaryTable(table) && (!catalog.databaseExists(table.database.get) || !catalog.tableExists(table))) {u} else {lookupTableFromCatalog(u)}}
}

  从上述ResolveRelations的实现中可以看到,当遍历逻辑算子树的过程中匹配到UnresolvedRelation节点时,对于本例会直接调用lookupTableFromCatalog方法从SessionCatalog中查表。实际上,该表在案例SQL查询的上一步中就已经创建好并以LogicalPlan类型存储在InMemoryCatalog中,因此lookupTableFromCatalog方法直接根据其表名即可得到分析后的LogicalPlan。
  需要注意的是,在Catalog查表后,Relation节点上会插入一个别名节点。此外,Relation中列后面的数字表示下标,注意其数据类型,age和id都默认设定为Long类型(“L”字符)。
  接下来,进入第2步,执行ResolveReferences规则,得到的逻辑算子树如下图所示。可以看到,其他节点都不发生变化,主要是Filter节点中的age信息从Unresolved状态变成了Analyzed状态(表示Unresolved状态的前缀字符单引号已经被去掉)。
请添加图片描述
  在ResolveReferences规则中与本例相关的匹配逻辑如以下代码所示。当碰到UnresolvedAttribute时,会调用LogicalPlan中定义的resolveChildren方法对该表达式进行分析。需要注意的是,resolveChildren并不能确保一次分析成功,在分析对应表达式时,需要根据该表达式所处LogicalPlan节点的子节点输出信息进行判断。在对Filter表达式中的age属性进行分析时,因为Filter的子节点Relation已经处于resolved状态,因此可以成功;而在对Project中的表达式name属性进行分析时,因为Project的子节点Filter此时仍然处于unresolved状态(注:虽然age列完成了分析,但是整个Filter节点中还有“18”这个Literal常数表达式未被分析),因此解析操作无法成功,留待下一轮规则调用时再进行解析。

object ResolveReferences extends Rule[LogicalPlan] {def apply(plan: LogicalPlan): LogicalPlan = plan resolveOperators {case q: LogicalPlan => q transformExpressionsUp {case u @ UnresolvedAttribute(nameParts) => val result = withPosition(u) {q.resolveChildren(nameParts, resolver).getOrElse(u) }resultcase UnresolvedExtractValue(child, fieldExpr) if child.resolved => ExtractValue(child, fieldExpr, resolver)}}
}

  完成第2步之后会调用TypeCoercion规则集中的ImplicitTypeCasts规则,对表达式中的数据类型进行隐式转换,这是Analyzed LogicalPlan生成的第3步,如下图所示。因为在Relation中,age列的数据类型为Long,而Filter中的数值“18”在Unresolved LogicalPlan中生成的类型为IntegerType,所以需要将“18”这个常数转换为Long类型。

请添加图片描述
  上述分析转换过程如上图所示,可以看到常数表达式“18”换为“cast(18 as bigint)”表达式(注:在Spark SQL类型系统中,BigInt对应Java中的Long类型)。ImplicitTypeCasts规则对于案例的逻辑算子树的处理过程如以下代码所示。对于BinaryOperator表达式,该规则会调用findTightestCommonTypeOfTwo找到对于左右表达式节点来讲最佳的共同数据类型。经过该规则的解析操作,可以看到上图中Filter节点已经变为Analyzed状态,节点字符前缀单引号已经被去掉。

object ImplicitTypeCasts extends Rule[LogicalPlan] {def apply(plan: LogicalPlan): LogicalPlan = plan resolvedExpressions {case b @ BinaryOperator(left, right) if left.dataType != right.dataType =>findTightestCommonTypeOfTwo(left.dataType, right.dataType).map { commonType =>if(b.inputType.acceptsType(commonType)) {val newLeft = if(left.dataType == commonType) left else Cast(left, commonType)val newRight = if(right.dataType = commonType) right else Cast(right, commonType)b.withNewChildren(Seq(newLeft, newRight))} else {b}}.getOrElse(b)}
}

  经过上述3个规则的解析之后,剩下的规则对逻辑算子树不起作用。此时逻辑算子树中仍然存在Project节点未被解析,接下来会进行下一轮规则的应用。第4步也是最后一步,再次执行ResolveReferences规则。
  如下图所示,经过上一步Filter节点已经处于resolved状态,因此逻辑算子树中的Project节点能够完成解析。Project节点的“name”被解析为“name#2”,其中“2”表示name在所有列中的下标。
请添加图片描述
  至此,Analyzed LogicalPlan就完全生成了。从上述步骤可以看出,逻辑算子树的解析是一个不断的迭代过程。实际上,用户可以通过参数(spark.sql.optimizer.maxIterations)设定RuleExecutor迭代的轮数,默认配置为50轮,对于某些嵌套较深的特殊SQL,可以适当地增加轮数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/141354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

打开word文档报错,提示HRESULT 0x80004005 位置: 部分: /word/comments.xml,行: 0,列: 0

某用户遇到这样一个奇怪的问题,就是回复完word的批注后,保存文档再打开就会报错,提示很抱歉,无法打开XXX,因为内容有问题。,详细信息提示HRESULT 0x80004005 位置: 部分: /word/comments.xml,行: 0,列: 0 c…

java学习part02一些特性

17-Java语言概述-Java语言的特点和JVM的功能_哔哩哔哩_bilibili 1.java优点 跨平台性 在jvm上运行 2.jvm 2.1实现跨平台性 不需要对每一种指令集编写编译器,只需要针对jvm编程,jvm会自动转换 2.2内存回收 内存溢出:用的内存太多已经占满了&…

No199.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

Seaborn数据可视化综合应用Basemap和Seaborn在线闯关_头歌实践教学平台

Seaborn数据可视化综合应用Basemap和Seaborn 第1关 Seaborn第2关 Seaborn图形介绍第3关 Basemap 第1关 Seaborn 任务描述 本关任务:编写一个绘制每个月销售总额的折线图。 编程要求 本关的编程任务是补全右侧上部代码编辑区内的相应代码,根据输入文件路…

微信小程序用户隐私API

用户隐私保护 由于用户隐私保护的政策执行,我们在调用涉及到用户隐私的API时,未更新用户隐私保护协议是无法直接调用的,小程序会默认判断是否更新用户隐私保护 ,并根据用户隐私保护中的协议来判断是否可以调用对应的API&#xff…

基于flask+bootstrap4实现的注重创作的轻博客系统项目源码

一个注重创作的轻博客系统 作为一名技术人员一定要有自己的博客,用来记录平时技术上遇到的问题,把技术分享出去就像滚雪球一样会越來越大,于是我在何三博客的基础上开发了[l4blog],一个使用python开发的轻量博客系统,…

rabbitMq创建交换机,以及路由键绑定队列教程

创建交换机: 创建队列: 创建路由,绑定到交换机: 补充: 创建新用户后,记得点进用户中,那两个set都点击一下; 还有配置代码连接的时候,连的端口为5672,可不…

STM32与RTOS的整合:实时操作系统在嵌入式开发中的应用

随着各种嵌入式系统应用的日益复杂和对实时性要求的提高,使用实时操作系统(RTOS)成为嵌入式开发中的一种重要选择。STM32微控制器作为一种强大的嵌入式处理器,与各种RTOS相结合,能够提供更高效、可靠并且易于维护的系统…

eNSP-打开华为USG6000V1防火墙web管理页面方法

一、本地打开防火墙web管理页面 1.先在ensp中启动USG6000V1防火墙,启动后,需要输入原始username和password(username:admin,password:Admin123),并修改原始密码后,才能配…

【中间件篇-Redis缓存数据库08】Redis设计、实现、redisobject对象设计、多线程、缓存淘汰算法

Redis的设计、实现 数据结构和内部编码 type命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)hash(哈希)、list(列表)、set(集合)、zset (有序集合),但这些只是Redis对外的数据结构。 实际上每种数据结构都有自己底层的…

【Spring Boot】034-Spring Boot 整合 JUnit

【Spring Boot】034-Spring Boot 整合 JUnit 文章目录 【Spring Boot】034-Spring Boot 整合 JUnit一、单元测试1、什么是单元2、什么是单元测试3、为什么要单元测试 二、JUnit1、概述简介特点 2、JUnit4概述基本用法 3、JUnit5概述组成 4、JUnit5 与 JUnit4 的常用注解对比 三…

计算机网络期末复习-Part5

1、CRC计算 看例题:待发送序列为101110,生成多项式为X31,计算CRC校验码 先在待发送序列末尾添加与生成多项式次数相同的零,在上述例子中,生成多项式是X^3 1,所以需要添加3个零,待发送序列变成…

一行JavaScrip可以做什么?

说在前面 JavaScript 提供了许多方便的方法和操作符来简化常见的任务,使得编程变得更加高效和便捷。无论是数学计算、字符串处理还是数据操作,JavaScript 都能帮助我们以简洁的方式实现所需功能。 代码 1、生成指定范围内的随机整数 const randomInt …

tensorboard报错解决:No dashboards are active for the current data set

版本:tensorboard 2.10.0 问题:文件夹下明明有events文件,但用tensorboard命令却无法显示。 例如: 原因:有可能是文件路径太长了,导致系统无法读取文件。在win系统中规定,目录的绝对路径不得超…

WordPress 文档主题模板Red Line -v0.2.2

此主题作为框架,做承载第三方页面之用,例如飞书文档等, 您可以将视频图片等资源放第三方文档上,通过使用此主题做目录用。 此主题使用前后端分离开发,也使用了一些技术尽量不影响正常的SEO,还望注意。 源码…

线性代数本质系列(一)向量,线性组合,线性相关,矩阵

本系列文章将从下面不同角度解析线性代数的本质,本文是本系列第一篇 向量究竟是什么? 向量的线性组合,基与线性相关 矩阵与线性相关 矩阵乘法与线性变换 三维空间中的线性变换 行列式 逆矩阵,列空间,秩与零空间 克莱姆…

XML Web 服务 Eclipse实现中的sun-jaxws.xml文件

说明 在sun-jaxws.xml文件,可以配置endpoint、handler-chain等内容。在这个文件中配置的内容会覆盖在Java代码中使用注解属性配置的的内容。 这个文件根据自己的项目内容修改完成以后,作为web应用的一部分部署到web容器中(放到web应用的WEB…

【机器学习】K近邻算法:原理、实例应用(红酒分类预测)

案例简介:有178个红酒样本,每一款红酒含有13项特征参数,如镁、脯氨酸含量,红酒根据这些特征参数被分成3类。要求是任意输入一组红酒的特征参数,模型需预测出该红酒属于哪一类。 1. K近邻算法介绍 1.1 算法原理 原理&a…

JavaScript从入门到精通系列第三十六篇:详解JavaScript中的事件监听和事件响应

文章目录 一:什么叫事件 1:概念 2:处理这个事件 (一):鼠标单机按钮 (二):鼠标双机按钮 (三):鼠标移动 3:写法弊端 4:Dom Event 二:监听事件 1:元素事…

域名反查Api接口——让您轻松查询域名相关信息

在互联网发展的今天,域名作为网站的唯一标识符,已经成为了企业和个人网络营销中不可或缺的一部分。为了方便用户查询所需的域名信息,API接口应运而生。本文将介绍如何使用挖数据平台《域名反查Api接口——让您轻松查询域名相关信息》进行域名…