零、序言
? spark1.6之后引入DataSet,一種基于RDD的高級抽象,在RDD之上加入了scheme信息,給RDD的元素的每一列提供了名稱和數(shù)據(jù)類型的標志。
? 同時DataSet還提供了更多的api,可以實現(xiàn)類似于sql的操作,而且在catalyst優(yōu)化器的優(yōu)化下我們的代碼將更加高效。
? 其實sql最最厲害的就是將邏輯和物理執(zhí)行分開,上層專注于讓程序員更好的表達數(shù)據(jù)的處理邏輯,下層專注于把邏輯執(zhí)行的更高效。而DataSet就是這上層用于表達數(shù)據(jù)處理邏輯的API的封裝。
一些術語約定(下文中有不理解再來看):
邏輯計劃:LogicalPlan --指生成的邏輯樹
物理計劃:這里指SparkPlan --對接了rdd,可以直接執(zhí)行,也是用樹表示的。
解析:parse --指對sql進行解析生成邏輯計劃
分析:analyze --指對邏輯計劃進行分析
一、整體執(zhí)行流程
大概經(jīng)過下面這幾個階段:
? 1.邏輯計劃生成:
1). spark.sql() 這種方式的話會涉及到sql的解析,解析之后就會生成邏輯計劃
2). 如果是直接在DataFrame的api上直接操作的話,使用的api將直接生成邏輯計劃
? 2.分析:生成的邏輯計劃將會通過解析器結合元數(shù)據(jù)信息(catalog)進行分析,比如識別到你讀的表是什么文件放在哪兒。
? 3.優(yōu)化:解析后的邏輯計劃會經(jīng)過優(yōu)化器進行優(yōu)化。 例如謂詞下推,比如將你的過濾條件下推到下一層,這樣就讓上一層少算點數(shù)據(jù)了。
? 4.生成物理計劃:將優(yōu)化后的邏輯計劃轉(zhuǎn)化成能執(zhí)行的物理計劃,也就是轉(zhuǎn)化成rdd的操作。比如你的inner join到底是用broadcatJoin還是用shuffleJoin。這里傳說會使用基于代價的優(yōu)化(RBO)對來進行優(yōu)化,但是源碼中只看到了收集統(tǒng)計信息。
二、邏輯的表示
在spark里面是如何用代碼來表示邏輯的呢 答案就是用樹結構來表示。也就是你寫的sql或者dataframe的操作,spark都會轉(zhuǎn)化成一顆邏輯樹,來看兩個例子。
2.1 例子
偽造點測試數(shù)據(jù):
# 首先我們先偽造兩張表 people score還有對映的兩個DataFrame變量
from pyspark.sql.functions import sum
people = spark.range(100).selectExpr("id","id+100 age")
score = spark.range(100).selectExpr("id","id+1 math_score","id+2 english_score")
score.cache().count()
people.cache().count()
spark.sql("use tmp")
people.write.saveAsTable("people")
score.write.saveAsTable("score")
DataFrame的操作:
people = spark.table("people")
score = spark.table("score")
tmp = people.join(score,score.id == people.id ).filter(people.age > 10)
tmp2 = tmp.select(score.id, (score.math_score+100+80+score.english_score).alias("v") )
res = tmp2.select(sum(tmp2.v))
sql語句的操作:
res = spark.sql("""
select sum(v)
from (
select score.id
,100+80+score.math_score+score.english_score as v
from people
join score
on people.id=score.id
where people.age>10
)tmp""")
解釋:
以上兩種雖然是不同的代碼寫出來的但是表達的邏輯其實都是一樣的,在spark里面就長下圖這樣。這樣一來就將我們通過代碼寫出來的邏輯轉(zhuǎn)化成了一棵邏輯樹,每一個節(jié)點都是一步操作。
2.2 邏輯樹在 spark 中的實現(xiàn)
上面的例子我們用圖完成了數(shù)據(jù)操作邏輯的表示,樹中的每個節(jié)點都相當于一步操作,只要我們獲取了最上層的那個源節(jié)點我們就能遍歷整棵樹了。也可以在這個源節(jié)點上層增加各種操作,形成更大的一棵樹。
spark里面邏輯節(jié)點都是TreeNode 主要用在三個地方
1.表達式的表示:用在解析sql的時候,用于表示sql。
2.邏輯計劃的表示:用來分析邏輯計劃 和 優(yōu)化邏輯計劃的時候
3.物理計劃(sparkplan)的表示: 用于執(zhí)行 有doExecute接口返回rdd
邏輯計劃和物理計劃都繼承自QueryPlan,節(jié)點有三種類型
1.有兩個子節(jié)點的叫binaryNode 例如join這樣的操作。
2.只有一個子節(jié)點的叫unaryNode,例如filter操作。
3.沒有子節(jié)點的葉子節(jié)點稱為leafNode,例如讀取數(shù)據(jù)的操作。
表達式可能會有三個的節(jié)點。
三、分析與優(yōu)化
分析和優(yōu)化spark里都是使用的規(guī)則來進行的,所有抽象出了一個規(guī)則執(zhí)行器的類(RuleExecutor)然后分析器(Analyzer)與優(yōu)化器(Optmizer)都是它的子類。看圖:
? Rule : 具體的規(guī)則,把一個LogicalPlan轉(zhuǎn)化成另一個LogicalPlan,實現(xiàn)的過程就大量利用了scala模式匹配的優(yōu)勢。比如PrushDownPredicate(謂詞下推)。
? Batch: 一批規(guī)則,有些規(guī)則需要結合起來使用的,所以規(guī)則都統(tǒng)一封裝成Batch。
? Strategy:每個Batch都有自己的執(zhí)行策略,比如有的只執(zhí)行一次,有個可能需要一直迭代執(zhí)行到結果不在改變?yōu)橹埂?br>
? 規(guī)則執(zhí)行器 (RuleExecutor):這個類里面就包含了很多Batch,用于使用這些Batch
四、轉(zhuǎn)化成物理計劃
上文說到物理計劃在spark里面使用SparkPlan這個類進行表示,它使用rdd來完成各項操作,是可執(zhí)行的計劃。那么LogicalPlan 是如何轉(zhuǎn)化成SparkPlan來進行執(zhí)行的呢。
GenericStrategy: 這個類就是用來把LogicalPlan 轉(zhuǎn)化成SparkPlan的。他也是基于規(guī)則的,子類就是不同規(guī)則的實現(xiàn),比如DataSourceStrategy就是用來處理一些數(shù)據(jù)源相關的LogicalPlan變成SparkPlan的。
QueryPlanner:計劃的執(zhí)行者,手上擁有很多LogicalPlan轉(zhuǎn)SparkPlan的策略集合。具體的實現(xiàn)是SparPlanner去干的。
五、例子 spark.read.jdbc().write.jdbc()
大家在留言中討論吧
廣告
加我信微 Zeal-Zeng 費免拉你進 知識星球、大數(shù)據(jù)社群、眾公號(曾二爺) 和優(yōu)秀的人一起學習