預(yù)備知識(shí)
先介紹在Spark SQL中兩個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu):Tree和Rule。
SparkSql的第一件事就是把SQLText解析成語法樹,這棵樹包含了很多節(jié)點(diǎn)對(duì)象,節(jié)點(diǎn)可以有特定的數(shù)據(jù)類型,同時(shí)可以有0個(gè)或者多個(gè)子節(jié)點(diǎn),節(jié)點(diǎn)在SparkSQL中的表現(xiàn)形式為TreeNode對(duì)象。舉個(gè)實(shí)際的例子:
- Literal(value: Int): 一個(gè)常量
- Attribute(name: String): 變量name
- Add(left: TreeNode, right: TreeNode): 兩個(gè)表達(dá)式的和
x + (1 + 2) 在代碼中的表現(xiàn)形式為:Add(Attribute(x), Add(Literal(1), Literal(2)))

而Rule則是應(yīng)用在Tree上的規(guī)則,通過模式匹配,匹配成功的就進(jìn)行相應(yīng)的規(guī)則變換,若不成功則繼續(xù)匹配子節(jié)點(diǎn),如在Optimizer模塊中有個(gè)常量累加的優(yōu)化規(guī)則,通過該規(guī)則,可以將兩個(gè)常量節(jié)點(diǎn)直接轉(zhuǎn)化為值相加后的一個(gè)常量節(jié)點(diǎn),如下圖:

可以看見先匹配第一個(gè)Add節(jié)點(diǎn)沒有匹配成功,再匹配其子節(jié)點(diǎn)Add成功了。
總流程圖
下圖便是SparkSql整個(gè)解析成RDD的流程圖,紅色部分便是SparkSql優(yōu)化器系統(tǒng)Catalyst,和大多數(shù)大數(shù)據(jù)SQL處理引擎設(shè)計(jì)基本相同(Impala、Presto、Hive(Calcite)等)。下面簡(jiǎn)述一下每個(gè)組成部分都做了什么,后續(xù)博客中會(huì)進(jìn)行詳解。

Parser
- sqlText先通過SparkSqlParser生成語法樹。
- Spark1版本使用的是scala原生的parser語法解析器,從2.x后改用的是第三方語法解析工具ANTLR4,只需要定制好語法,可以通過插件自動(dòng)生成對(duì)應(yīng)的解析代碼。
- 然后通過AstBuilder配合antlr的visitor模式自主控制遍歷Tree,將antlr里面的節(jié)點(diǎn)都替換成catalyst(優(yōu)化器系統(tǒng))里面的類型,所有的類型都繼承了TreeNode特質(zhì),TreeNode又有子節(jié)點(diǎn)children: Seq[BaseType],便有了樹的結(jié)構(gòu)。
- 此過程解析完后形成的AST(抽象語法樹)為 unresolved LogicalPlan。
Analyzer
- 上個(gè)步驟還只是把sql字符串通過antlr4拆分并由SparkSqlParser解析成各種LogicalPlan(TreeNode的子類),每個(gè)LogicalPlan究竟是什么意思還不知道。
- 接下來就需要通過Analyzer去把不確定的屬性和關(guān)系,通過catalog和一些適配器方法確定下來,比如要從Catalog中解析出表名user,是臨時(shí)表、臨時(shí)view,hive table還是hive view,schema又是怎么樣的等都需要確定下來。
- 將各種Rule應(yīng)用到Tree之上的真正執(zhí)行者都是RuleExecutor,包括后面的Optimizer 也繼承了RuleExecutor, 解析的套路是遞歸的遍歷,將新解析出來的LogicalPlan來替換原來的LogicalPlan。
- 此過程解析完后形成的AST為 resolved LogicalPlan。若沒有action操作,后續(xù)的優(yōu)化,物理計(jì)劃等都不會(huì)執(zhí)行。
Optimizer
- 這個(gè)步驟就是根據(jù)大佬們多年的SQL優(yōu)化經(jīng)驗(yàn)來對(duì)SQL進(jìn)行優(yōu)化,比如謂詞下推、列值裁剪、常量累加等。
- Optimizer 也繼承了RuleExecutor,并定義了一批規(guī)則,和Analyzer 一樣對(duì)輸入的plan進(jìn)行遞歸處理,此過程解析完后形成的AST為 optimized LogicalPlan。
SparkPlanner
通過優(yōu)化后的LogicalPlan還只是邏輯上的,接下來需要通過SparkPlanner 將optimized LogicalPlan應(yīng)用到一系列特定的Strategies上,即轉(zhuǎn)化為可以直接操作真實(shí)數(shù)據(jù)的操作及數(shù)據(jù)和RDD的綁定等,此過程解析完后形成的AST為 PhysicalPlan。
prepareForExecution
此模塊將 physical plan 轉(zhuǎn)化為 executable physical plan,主要是插入 shuffle 操作和 internal row 的格式轉(zhuǎn)換。
execute
最后調(diào)用SparkPlan的execute()執(zhí)行計(jì)算。每個(gè)SparkPlan里面都有execute的實(shí)現(xiàn),一般都會(huì)遞歸調(diào)用children的execute()方法,最后便會(huì)觸發(fā)整個(gè)Tree的計(jì)算。
最后上個(gè)流程圖

后續(xù)會(huì)對(duì)每個(gè)模塊進(jìn)行詳細(xì)解析。