sql怎么查看rule绑定_Spark源码阅读-LogicalPlan[Analyzed LogicalPlan]Rule

一:阅读Analyzer前的两个概念【Catalog】【Rule】

2,Rule

源码路径如下所示:

9a01adbfa9b1cfba613aac05baefbc3c.png

Unresolved LogicalPlan未解析逻辑算子树(绑定,解析,优化等),主要的方法其实都是基于规则的(Rule)

Rule.scala是个抽象类,子类实现时候重写apply实现特定处理逻辑

ca1ea76f8f298138313dccbe15da40e9.png

每个类的Rule实现都是通过RuleExcutor.scala来调用的,RuleExecutor[TreeType <: TreeNode[_]],核心是Strategy和Batch如下图所示:

fa6258f840d52d64b2283477de78d888.png

4b8dfa9e4e37cd9bb6309fd3eda0f34d.png

02235b767a1892e1f5846ba9fb2fa053.png

26727c95c4ad5139896af431a30c972d.png

e612e48cd0d2a96a13716eeb42dbf7b1.png

QueryExecutionMetering.scala重置和统计耗时一个工具类

e371053f7983f505e5dda14e7613302f.png

package.scala最近一次更新大概是五年前了,是个object

57dcfff630d63a3402d64d807dfd2216.png

Rule其实更多是一个接口规范定义,但是具体每个子类是怎么实现继承 重写覆盖方法,会是个很好的学习课题!

OK其实阅读完了Catalog和Rule,返回阅读Analyzed LogicalPlan会理解上轻松很多~

我们返回到这张大图,整体上迅速清晰下【这张图会贯穿在整个spark sql源码阅读过程中】:

68625821c2e172a3969d85f973aa43c4.png

Analyzer解析前是unresolved LogicalPlan 解析后是resolved LogicalPlan。解析过程中主要是用到了RuleExcuter【就是Rule体系接口类实现】

OK清楚这样关系,往下梳理吧。。。

Analyzer.scala 继承了RuleExecutor,并且传递了SessionCatalog。

3a779b5e1d22b98371a92bec01b164a9.png

除此之外还有两个类SimpleAnalyzer已处理简单实现,AnalysisContext一种在分析期间保持环境状态的方法。

5fa473ed2992796f6ed882e5042fb224.png

Analyzer使用于对最初的unresolved logical plan转化成为logical plan。这部分的分析会涵盖整个analysis package。作用是把未确定的属性和关系,通过Schema信息(来自于SessionCatalog类)和方法注册类来确定下来。一共需要经过三步骤。但是在整体梳理哪三步前先梳理下Batch。

Analyzer中这里实现了一些列的Batch,具体大类分为{Hints【提示】;Simple Sanity Check【简单健康检查】;Substitution【替换规则】;Resolution【通用解析规则】;Post-Hoc Resolution【特殊解析规则】;View【视图】;Nondeterministic【非确定性】;UDF;UpdateNullability【不可更新规则】;Subquery【子查询】;Cleanup【清除】}具体源码如下:

d0e6312ec11adc4abf4bfe643cbfee66.png

eb589a10660daa1cc2a7d930fcef2c87.png

1,CTESubstitution 规则:substitution[替换],就是用来处理 With 语旬的 。 在遍历逻辑算子树的过程中,当匹配到 With( child, relations )节点时,将子 LogicalPlan 替换成解析后的 CTE a 由于 CTE 的存在,SparkSqlParser 对 SQL 语句从左向右解析后会产 生多个 LogicalPlan 。 这条规则的作用是将多个 LogicalPlan 合并成一个 LogicalPlan 。

7b5c2cba7bc80e7c82ddf80a6fc77c5b.png

2,WindowsSubstitution 规则:对当前的逻辑算子树进行查找, 当 匹配到WithWindow Definition (windowDefinitions, child )表达式时,将其子节点中未解析的窗口函数表达式( UnresolvedWindow Expression )转换成窗口函数表达式(WindowExpression )

3156b90e3893bcdaf9699d8e26dbc2ca.png

3,ResolveAliases 规则:解析别名

093f65e885cfd214d84dae8f579f755d.png

4,ResolveGroupingAnalytics 规则:解析分组分析,核心rollupExprs和cubeExprs

1dabf8a641b2a0eb08eeb5b03caa8924.png

这里有个奇怪的名字cubeExprs和cubeExprs0,居然出现了初级程序猿才会写的命名方法~是魔鬼吗~ 不过这样做目的是调用了toIndexedSeq方法实现懒加载,避免序列化问题。

391b3cea6ba48bdfe3913c867e088a83.png

d2eadfac7d2e046d5f55ff4684ba4725.png

b40bb08135b231a7c5a49f754300fe77.png

92e429a7b155f60f8edaafeba987592b.png

5,ResolvePivot 规则:透视操作重塑数据,透视是一种聚合操作。

什么是数据重塑:详情《spark数据集重塑》[1],看完这篇文章就很好理解这种规则了。

先看一个简单例子:比如,我们想对A列和B列进行分组,然后在C列上进行透视操作并对D列数据进行求和

df.groupBy(“A”, “B”).pivot(“C”, Seq(“small”, “large”)).sum(“D”)

613fa91074bc4c03212b295e92fd1dca.png

dea3896b6ea041efbb6d2c6e5c65dca0.png

OK是不是很简单,源码也就罗就比较清晰了

1131170e09c5ad77048ae9e45a659752.png

aea0dc21d53dd496ef6b328266992d20.png

622e61c78f1d0984e634481986586d1f.png

6,ResolveRelations 规则:分解关系,此规则是通过catalog替换掉UnresolvedRelation

即可以从中获取到database和table的名字,接下来从入口方法apply看是怎么一步一步替换掉的:

2c0ed0d3a1719a2a86f7e19db680da05.png

642599b810b59252511c715287ef381a.png

第一个case,然后会调用lookupTableFromCatalog方法从catalog中找关系,此方法最终调用了SessionCatalog的lookupRelation方法:

c2f94be346fd708af4246ee5f760de8c.png

catalog是SessionCatalog.scala的lookupRelation方法如下

56546225571ad046504d75036c774f8c.png

返回去看resolveRelation方法之后,返回的plan是已经和实际元数据绑定好的plan,可能是从globalTempViewManager直接获取的,可能是从tempTables直接获取,也可能是从externalCatalog获取的元数据。

再看第二个case 若plan是InsertIntoTable类型并且其对应的table还未绑定,则调用lookupTableFromCatalog方法与catalog进行analyze之后应用到Rule EliminateSubqueryAliases:刚好继续这个规则阅读

7,EliminateSubqueryAliases规则: 删除运算符

751bfe948959d6d1baf363ff73d82e6f.png

8,ResolveReferences 规则: 未解析属性,转换为逻辑树子节点中的具体属性。

0b3b72629100d29920019d9d1ce65966.png

dedupRight方法:为具有不同表达式ID的右子级生成新的逻辑计划对于所有冲突的属性。

conflictingAttributes 左右树属性冲突存储在变量中

其中主要处理一下冲突:MultiInstanceRelation【多次关系】;SerializeFromObject【对象序列化】;Project【项目别名冲突】;Aggregate【聚合】;FlatMapGroupsInPandas【分组后扁平化冲突】;Generate【生成】;Window【窗口】

5c67173d974969cc74cd3253eb089ea6.png

03a55d36faceb04994fe27f4df6164ea.png

处理好以上冲突之后,然后collect之后,只处理第一种情况,其他情况下一个通行证上解决如下:

84a8bb405049a9d59677886d85488deb.png

dedupAttr方法就比较简单,把重复数据属性删除掉

5626a67813c14064ea49eed411154dd5.png

dedupOuterReferencesInSubquery方法:外部计划可能已经被消除了重复,这方法是更新外部引用达到能引用到消除的重复数据的属性。这个方法还挺重要的!

看下简单例子:样例SQL如下:

d94c65637b0271d110150bdd330f1378.png

SQL转为Unresolved LogicalPlan如下

2dfdb201dc0c0749b597f686baad12c4.png

再经过resolvedReference处理之后如下

a4fea171c1ff43ba92245b68148bb98b.png

a7a88eb149b767dad254cf855c0f3064.png

resolveExpressionTopDown 方法:通过自顶向下遍历输入表达式来解析属性并提取值表达式。

例如

05ace9f0cd74e48587b32e937355946f.png

在上面例子中,x和i都是作为lamda函数一个属性在[[ResolveLambdaVariables]]

在这个例程中,未解析的属性是从输入计划的子属性。

1627652485e617430f7d6083dd149e9c.png

apply方法:继承覆盖了父类方法

这个是判断是否传入的表达式包含[[Star]]

36d20e34854b38481ecc2c480c0550ea.png

OK看看apply干啥了:

9cf67ac98853e4b9db230c3590ef308f.png

1a09643c2a6aa240f091771aaf754033.png

72224ea0d8e25b26eb50fe28aefacd5b.png

buildExpandedProjectList方法:为 Project/Aggregate 构建一个项目列表,并尽可能扩展

00712de68db7376a7b91a0285b9de372.png

expandStarExpression方法:在子节点中展开匹配的*'s的属性

b5238eb8000ff14f15c64f77e6dcd654.png

resolveLiteralFunction方法:当属性不可解析时,文本函数不要求用户在调用它们时指定大括号,我们尝试将其解析为文本函数。简单的一个功能看源码~

320b018c78dcdeb20cee54c9062844d9.png

resolveExpressionBottomUp方法:通过遍历自下而上的输入表达式。为了解析嵌套的复杂类型字段或者说,此函数使用“throws”参数控制何时引发分析例外。

来个样例:

6e29d1784a902facaeef5520516ad654.png

在上面的例子中,需要先解决B中的问题,然后才能解决D中的问题。考虑到我们正在进行自下而上的遍历,它将首先尝试解析d并失败,因为b尚未解析。如果“throws”为false,则此函数将通过返回原始属性来处理异常。在这种情况下,“d”将在“b”解析之后的后续过程中解析。.

56b43919702c4b6b27b21a2caffe8988.png

9,ResolveOrdinalInOrderByAndGroupBy 规则:spark2.0之后新特性,此规则用于将序号位置转换为选择列表中相应的表达式。

0d49105df1b894e260dc51173b8c562f.png

10,ResolveAggAliasInGroupBy 规则:将分组键中的未解析表达式替换为select子句中的已解析表达式。

978688a23175ad58c3994b580e249e8e.png

11,ResolveMissingReferences 规则: 解决缺少的引用,常规的SQL语法里面有很多属性是没有显性的表示出来的,但是转换为projection时候需要这个规则把这种属性解析出来。

a271d7ff29fa2c3e152198a2ff9ef2c8.png

resolveExprsAndAddMissingAttrs 方法:此方法尝试递归解析表达式并查找缺少的属性。特别是当“sort”“filter”中使用的表达式包含未解析的属性或子输出中缺少的已解析属性时。

e9ce1596b34ca330225e450c0fb96190.png

12,LookupFunctions :根据函数标识符执行简单的存在性检查,以快速识别未定义的函数,此规则不会尝试解析[[未解析函数]]。为了避免重复的外部函数查找,外部函数标识符将存储在本地哈希集ExternalFunctionNameset中。

d29d1217945381f0e22e88470cf7c830.png

13,ResolveFunctions: 解析函数,把 [[UnresolvedFunction]]s 转换为具体的[[Expression]]s.

9d6b97e3114b8804271b5732dde0dc21.png

14,ResolveSubquery规则:此规则解析并重写表达式中的子查询,resolveSubQuery;resolveSubQueries两个核心方法,分别是解析子查询表达式中引用的子查询计划和解析子查询

92694c3be18a7282f57e01dc65edebd3.png

64bba9e04dc2016b6f68e3961815090a.png

8e6fee1ca30eccdac052db0ff7ca979f.png

15,ResolveSubqueryColumnAliases规则:把未解析的别名转换为projections名称

5a73e2e350bd87c765a34445044c3285.png

16,GlobalAggregates :把projections中包含聚合表达式的转为聚合

dff2bfabe0834f6f55e80285f39f3b80.png

17,ResolveAggregateFunctions规则:此规则查找不在聚合运算符中的聚合表达式。

f43bc2824feac7494ad36c508ddffcac.png

8ed461d6e62d8658006683625ff9c74b.png

ce488a3eaaef345b69c8b0f33989833a.png

18,ExtractGenerator 规则:从[[Project]]运算符的项目列表中提取[[Generator]],并在[[Project]]下创建[[Generate]]运算符。

a2d16a4d7e15b77a75e002b53b38342b.png

c353e3939d99ac56af788df49b6e1ad7.png

b5a12a1977b779b73422cf41cded0e37.png

19,ResolveGenerate规则:重写表,生成需要以下一个或多个表达式才能解析的表达式:

686e2edbc8b019610f6f66b00f297b7a.png

20,ExtractWindowExpressions规则:这个规则特别重要

08b2871f0954677276e1b938d0f21bed.png

extract方法:

8c351f4a6db514010e22be288ed176d9.png

f677662cf7eefa04c7b350940a3f554f.png

b4ad94f56a69bc6cd89091d95dad8b07.png

addWindow方法:为窗口表达式添加运算符。每个窗口操作员处理一个窗口规范。

7588ea898c94db66d6cdc38aaa12630f.png

5713b7d2e2a514f195658afd5ec577fa.png

db6c67278a09ef16e058d141594dd73e.png

9f7c56932c77cf6df0e889ce9f0d1026.png

6df8f6203ec5ea53865f6dc6dd8e3080.png

21,PullOutNondeterministic规则:提取非确定表示式.

0d9c68a91dbefe71ef5502ba50cc9585.png

22,ResolveRandomSeed规则:为随机生成器设置种子

8d02e9c51b23031771b0135191a6b75c.png

23,HandleNullInputsForUDF规则:UDF函数空校验

f554046c28d29638522b8e343c5a930e.png

24,ResolveWindowFrame规则:检查并为所有窗口功能添加适当的窗框。

ba87f3f4599de334b6879ed7da33abcc.png

25,ResolveWindowOrder规则:检查并添加序列

8e29e557a61c1601230beca78a9b6018.png

26,ResolveNaturalAndUsingJoin规则:通过基于两侧的输出计算输出列来删除自然联接或使用联接,然后在正常联接上应用项目以消除自然联接或使用联接。

3a6501c987037ea63911bd06c93a657a.png

27,ResolveOutputRelation规则:从逻辑计划中的数据解析输出表的列。

* - 按名称写入时重新排序列

* - 数据类型不匹配时插入安全强制转换

* - 当列名不匹配时插入别名

* - 检测与输出表不兼容的计划并引发AnalysisException

7663483b8d7bc2d1703019a8b63942d2.png

28,ResolveDeserializer规则:用已解析为给定输入属性的反序列化表达式替换[[UnsolvedDeserializer]]。

685badc4f0ab4b655ea8f61ca30b4aa1.png

29,ResolveNewInstance规则:解析newInstance

1393ebf7d33abd84da956425a344a3e5.png

30,ResolveUpCast规则:将[[UpCast]]表达式替换为[[Cast]]

9597073183768b33d2493728ade11c97.png

31,EliminateUnions规则:如果计划只有一个子级,则从该计划中删除[[Union]]运算符。

ee88cfe78366077835a7f59e4111b71f.png

32,CleanupAliases规则:清理别名

92f242d190a2efee361fd94ba33ab704.png

33,EliminateEventTimeWatermark规则:批查询中忽略事件时间水印,这仅在结构化流中受支持。

f59fb57061e0f35f3127035ecdbbad26.png

34,TimeWindowing规则:使用Expand运算符将时间列映射到多个时间窗口。

66a5c92f803a3459a7478c1cb1d0223a.png

ad3791985dc6ddc399853cb9277311ef.png

35,ResolveCreateNamedStruct规则:如果包含[[nameplaceholder]]s,请解析[[createnamedstruct]]。

6370a8cccf37e16aa11cc58f10a2af30.png

36,UpdateOuterReferences规则:更新输出引用

看个样例:

fad07634ef66e5bec7abfc875ebff3d5.png

470a958f6e62041511a7f89015be34db.png

7b98c5ef61b224e6f6dd98f2045c4960.png

至此 把Analyzer.scala中36个规则全部阅读完。其实代码阅读还是比较枯燥的。期间的逻辑基本面都是每个规则本省逻辑点,还没有串联起来,我们还是要跳出来思路,就是这些规则怎么和Unresolved LogicalPlan结合,然后经过这些解析Analyzer中规则,然后变成Resolved LogicalPlan。中间到底有哪些没有搞清楚地方呢?明天整体串起来做个小结。

参考

  1. ^文引自Andrew Ray博士在Silicon Valley Data Science网站上发表的博客 http://www.023dns.com/server_ecs/5104.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值