Hive源碼學(xué)習(xí)——Driver

HiveQL解析流程:


1.Hive根據(jù)Antlr定義的詞法、語(yǔ)法規(guī)則完成詞法、語(yǔ)法分析將HQL解析為AST Tree;
2.遍歷AST Tree,抽象出查詢的基本組成單元Query Block;
3.遍歷Query Block解析為操作樹(shù)Operator Tree(邏輯執(zhí)行計(jì)劃);
4.邏輯優(yōu)化器進(jìn)行操作樹(shù)變換,合并多余的ReduceSinkOperator,減少shuffle;
5.遍歷Operator Tree,將操作樹(shù)翻譯為對(duì)應(yīng)的MapReduce任務(wù);
6.物理優(yōu)化器進(jìn)行MapReduce任務(wù)變換,生成最終的執(zhí)行計(jì)劃。

SQL編譯源碼分析

ql文件目錄下可以找到以下5個(gè)文件,具體路徑為源碼中的ql/src/java/org/apache/hadoop/hive/ql/parse下:
SelectClauseParser.g:select從句語(yǔ)法解析
FromClauseParser.g:from從句語(yǔ)法解析
HiveLexer.g:詞法分析,定義了所有用到的token
HiveParser.g:語(yǔ)法解析
IdentifiersParser.g:自定義函數(shù)的解析

hive源碼中語(yǔ)法文件之間的關(guān)系:

run方法

Driver類的入口是Driver.run(String comman),重點(diǎn)是調(diào)用了Driver.runInternal(String command, boolean alreadyCompiled)。


runInternal方法

compileInternal方法

其中核心為調(diào)用compile()方法進(jìn)行表編譯,主要是將SQL字符串翻譯成AST Tree,然后翻譯成可執(zhí)行的task樹(shù),然后再優(yōu)化執(zhí)行樹(shù)。


compile方法:

主要是調(diào)用ParseUtils.parse()方法。


ParseUtils.parse()方法:

parse()方法返回的是AST Tree[抽象語(yǔ)法樹(shù)]信息。


ParseDriver類的parse方法

parse方法返回的是HiveParser.statement_return也是一顆抽象語(yǔ)法樹(shù),具體語(yǔ)法樹(shù)的接口可以參見(jiàn)相應(yīng)的HiveParse.g文件
注:HiveParse.g文件的具體目錄及作用可以查看SQL編譯源碼分析模塊


Driver方法:

得到語(yǔ)法樹(shù)以后,返回到Driver類中,會(huì)根據(jù)語(yǔ)法樹(shù)根節(jié)點(diǎn) 的類型來(lái)選擇相應(yīng)的SemanticAnalyzer


SemanticAnalyzerFactory.get(queryState, tree)方法

主要根據(jù)根節(jié)點(diǎn)的語(yǔ)法樹(shù)類型來(lái)選擇相應(yīng)的analyzer,具體的選擇analyzer代碼如下。
對(duì)于DDL操作,得到的就是DDLSemanticAnalyzer,對(duì)于一般的insert(hive中村select語(yǔ)句會(huì)翻譯成一個(gè)insert tmpDirectory的語(yǔ)句)得到的就是SemanticAnalyzer。



然后調(diào)用analysis()方法將抽象語(yǔ)法樹(shù)翻譯成可執(zhí)行的執(zhí)行計(jì)劃。


BaseSemanticAnalyzer.analyze()方法:


由于BaseSemanticAnalyzer是抽象類,所以我們應(yīng)該找它的繼承類,得到SemanticAnalyzer類。




SemanticAnalyzer類是對(duì)整棵樹(shù)進(jìn)行解析(深度優(yōu)先探索),然后將抽象語(yǔ)法樹(shù)翻譯成一個(gè)QB(query block)。

genOPTree()方法

一個(gè)QB類


QB中有兩個(gè)重要的變量:qbm和qbp都有QB的引用,這樣組成了一顆樹(shù)。
genOPTree()方法返回的是一個(gè)Operator類,如下圖所示:



從代碼中可以看到很多與children和parent相關(guān)的變量和方法,這是一個(gè)有向五環(huán)圖(DAG)。然后進(jìn)行邏輯優(yōu)化,使用Optimizer.initialize()方法。

Optimizer.initialize()

有以下優(yōu)化器。



在SemanticAnalyzer.analyzeInternal方法中最終會(huì)調(diào)用compiler.compile()方法,把可執(zhí)行的計(jì)劃存儲(chǔ)在rootTasks中,Task的executeTask()方法是可以直接執(zhí)行的,最終實(shí)際的執(zhí)行也是調(diào)用每個(gè)task的executeTask方法,依賴以及調(diào)度是在上層控制的,Task的繼承關(guān)系如下:




Task是一個(gè)樹(shù)形結(jié)構(gòu),每個(gè)task有一堆child task,這些child是在執(zhí)行順序上依賴自己的task,rootTask中存儲(chǔ)的就是整個(gè)執(zhí)行計(jì)劃中需要最開(kāi)始執(zhí)行的task list,一顆“倒著的執(zhí)行依賴樹(shù)”。

Driver類中,執(zhí)行task,Driver.execute()為入口。
將可執(zhí)行的task放入runnable中,初始化root task list,runnable表示正在運(yùn)行running的task。
launchTask()方法中,啟動(dòng)task,其實(shí)就是調(diào)用task的executeTask()方法。
注:從Driver類的run()進(jìn)入,找到runInternal()方法,其中會(huì)執(zhí)行execute()


execute():

繼續(xù)追,然后在execute()方法中找到launchTask()方法。



找出執(zhí)行完成的task,然后遍歷該task的子task,選出可執(zhí)行的(pre task已經(jīng)執(zhí)行完成)task放入runnable中,重復(fù)上一步。
對(duì)于一些有多個(gè)pre task的child task,會(huì)在最后一個(gè)pre task執(zhí)行完成后被啟動(dòng),所以在執(zhí)行到這里時(shí)會(huì)在child中過(guò)濾掉。



至此,對(duì)Driver.java的一個(gè)簡(jiǎn)單分析結(jié)束。

參考:

https://www.cnblogs.com/swordfall/p/13426569.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容