HiveQL解析流程:
1.Hive根據(jù)Antlr定義的詞法、語法規(guī)則完成詞法、語法分析將HQL解析為AST Tree;2.遍歷AST Tree,抽象出查詢的基本組成單元Query Block;3.遍歷Query Block解析為操作樹Operator Tree(邏輯執(zhí)行計劃);4.邏輯優(yōu)化器進行操作樹變換,合并多余的ReduceSinkOperator,減少shuffle;5.遍歷Operator Tree,將操作樹翻譯為對應(yīng)的MapReduce任務(wù);6.物理優(yōu)化器進行MapReduce任務(wù)變換,生成最終的執(zhí)行計劃。
SQL編譯源碼分析
ql文件目錄下可以找到以下5個文件,具體路徑為源碼中的ql/src/java/org/apache/hadoop/hive/ql/parse下:SelectClauseParser.g:select從句語法解析FromClauseParser.g:from從句語法解析HiveLexer.g:詞法分析,定義了所有用到的tokenHiveParser.g:語法解析IdentifiersParser.g:自定義函數(shù)的解析
hive源碼中語法文件之間的關(guān)系:
run方法
Driver類的入口是Driver.run(String comman),重點是調(diào)用了Driver.runInternal(String command, boolean alreadyCompiled)。
runInternal方法
compileInternal方法
其中核心為調(diào)用compile()方法進行表編譯,主要是將SQL字符串翻譯成AST Tree,然后翻譯成可執(zhí)行的task樹,然后再優(yōu)化執(zhí)行樹。
compile方法:
主要是調(diào)用ParseUtils.parse()方法。
ParseUtils.parse()方法:
parse()方法返回的是AST Tree[抽象語法樹]信息。
ParseDriver類的parse方法
parse方法返回的是HiveParser.statement_return也是一顆抽象語法樹,具體語法樹的接口可以參見相應(yīng)的HiveParse.g文件注:HiveParse.g文件的具體目錄及作用可以查看SQL編譯源碼分析模塊
Driver方法:
得到語法樹以后,返回到Driver類中,會根據(jù)語法樹根節(jié)點 的類型來選擇相應(yīng)的SemanticAnalyzer
SemanticAnalyzerFactory.get(queryState, tree)方法
主要根據(jù)根節(jié)點的語法樹類型來選擇相應(yīng)的analyzer,具體的選擇analyzer代碼如下。對于DDL操作,得到的就是DDLSemanticAnalyzer,對于一般的insert(hive中村select語句會翻譯成一個insert tmpDirectory的語句)得到的就是SemanticAnalyzer。
然后調(diào)用analysis()方法將抽象語法樹翻譯成可執(zhí)行的執(zhí)行計劃。
BaseSemanticAnalyzer.analyze()方法:
由于BaseSemanticAnalyzer是抽象類,所以我們應(yīng)該找它的繼承類,得到SemanticAnalyzer類。
SemanticAnalyzer類是對整棵樹進行解析(深度優(yōu)先探索),然后將抽象語法樹翻譯成一個QB(query block)。
genOPTree()方法
一個QB類
QB中有兩個重要的變量:qbm和qbp都有QB的引用,這樣組成了一顆樹。genOPTree()方法返回的是一個Operator類,如下圖所示:
從代碼中可以看到很多與children和parent相關(guān)的變量和方法,這是一個有向五環(huán)圖(DAG)。然后進行邏輯優(yōu)化,使用Optimizer.initialize()方法。
Optimizer.initialize()
有以下優(yōu)化器。
在SemanticAnalyzer.analyzeInternal方法中最終會調(diào)用compiler.compile()方法,把可執(zhí)行的計劃存儲在rootTasks中,Task executeTask()方法是可以直接執(zhí)行的,最終實際的執(zhí)行也是調(diào)用每個task executeTask方法,依賴以及調(diào)度是在上層控制的,Task的繼承關(guān)系如下:
Task是一個樹形結(jié)構(gòu),每個task有一堆child task,這些child是在執(zhí)行順序上依賴自己的task,rootTask中存儲的就是整個執(zhí)行計劃中需要最開始執(zhí)行的task list,一顆“倒著的執(zhí)行依賴樹”。
Driver類中,執(zhí)行task,Driver.execute()為入口。將可執(zhí)行的task放入runnable中,初始化root task list,runnable表示正在運行running的task。launchTask()方法中,啟動task,其實就是調(diào)用task的executeTask()方法。注:從Driver類的run()進入,找到runInternal()方法,其中會執(zhí)行execute()
execute():
繼續(xù)追,然后在execute()方法中找到launchTask()方法。
找出執(zhí)行完成的task,然后遍歷該task的子task,選出可執(zhí)行的(pre task已經(jīng)執(zhí)行完成)task放入runnable中,重復(fù)上一步。對于一些有多個pre task child task,會在最后一個pre task執(zhí)行完成后被啟動,所以在執(zhí)行到這里時會在child中過濾掉。
至此,對Driver.java的一個簡單分析結(jié)束。
舉報/反饋以上就是【我怎么沒早點發(fā)現(xiàn)!怎么可以錯過(hive源碼debug)hive編程指南-Hive深度學(xué)習(xí)|源碼解析——Driver】的全部內(nèi)容。
評論