HiveQL解析流程:
1.Hive根據(jù)Antlr定義的詞法、語法規(guī)則完成詞法、語法分析將HQL解析為AST Tree缆娃;
2.遍歷AST Tree捷绒,抽象出查詢的基本組成單元Query Block;
3.遍歷Query Block解析為操作樹Operator Tree(邏輯執(zhí)行計劃)贯要;
4.邏輯優(yōu)化器進(jìn)行操作樹變換暖侨,合并多余的ReduceSinkOperator,減少shuffle崇渗;
5.遍歷Operator Tree它碎,將操作樹翻譯為對應(yīng)的MapReduce任務(wù);
6.物理優(yōu)化器進(jìn)行MapReduce任務(wù)變換显押,生成最終的執(zhí)行計劃扳肛。
SQL編譯源碼分析
ql文件目錄下可以找到以下5個文件,具體路徑為源碼中的ql/src/java/org/apache/hadoop/hive/ql/parse下:
SelectClauseParser.g:select從句語法解析
FromClauseParser.g:from從句語法解析
HiveLexer.g:詞法分析乘碑,定義了所有用到的token
HiveParser.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()方法進(jìn)行表編譯兽肤,主要是將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類是對整棵樹進(jìn)行解析(深度優(yōu)先探索)琳袄,然后將抽象語法樹翻譯成一個QB(query block)江场。
genOPTree()方法
一個QB類
QB中有兩個重要的變量:qbm和qbp都有QB的引用,這樣組成了一顆樹挚歧。
genOPTree()方法返回的是一個Operator類扛稽,如下圖所示:
從代碼中可以看到很多與children和parent相關(guān)的變量和方法,這是一個有向五環(huán)圖(DAG)滑负。然后進(jìn)行邏輯優(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()進(jìn)入,找到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é)束。
參考: