轉載:http://www.cnblogs.com/yaojingang/p/5446310.html
在了解了MapReduce實現SQL基本操作之后,我們來看看Hive是如何將SQL轉化為MapReduce任務的,整個編譯過程分為六個階段:
- Antlr定義SQL的語法規則,完成SQL詞法,語法解析,將SQL轉化為抽象語法樹AST Tree
- 遍歷AST Tree,抽象出查詢的基本組成單元QueryBlock
- 遍歷QueryBlock,翻譯為執行操作樹OperatorTree
- 邏輯層優化器進行OperatorTree變換,合并不必要的ReduceSinkOperator,減少shuffle數據量
- 遍歷OperatorTree,翻譯為MapReduce任務
- 物理層優化器進行MapReduce任務的變換,生成最終的執行計劃
下面分別對這六個階段進行介紹
Phase1 - SQL詞法,語法解析
Antlr
Hive使用Antlr實現SQL的詞法和語法解析。Antlr是一種語言識別的工具,可以用來構造領域語言。
這里不詳細介紹Antlr,只需要了解使用Antlr構造特定的語言只需要編寫一個語法文件,定義詞法和語法替換規則即可,Antlr完成了詞法分析、語法分析、語義分析、中間代碼生成的過程。
Hive中語法規則的定義文件在0.10版本以前是Hive.g一個文件,隨著語法規則越來越復雜,由語法規則生成的Java解析類可能超過Java類文 件的最大上限,0.11版本將Hive.g拆成了5個文件,詞法規則HiveLexer.g和語法規則的4個文件 SelectClauseParser.g,FromClauseParser.g,IdentifiersParser.g,HiveParser.g。
抽象語法樹AST Tree
經過詞法和語法解析后,如果需要對表達式做進一步的處理,使用 Antlr 的抽象語法樹語法Abstract Syntax Tree,在語法分析的同時將輸入語句轉換成抽象語法樹,后續在遍歷語法樹時完成進一步的處理。
下面的一段語法是Hive SQL中SelectStatement的語法規則,從中可以看出,SelectStatement包含select, from, where, groupby, having, orderby等子句。
(在下面的語法規則中,箭頭表示對于原語句的改寫,改寫后會加入一些特殊詞標示特定語法,比如TOK_QUERY標示一個查詢塊)
Phase2 - SQL基本組成單元QueryBlock
AST Tree仍然非常復雜,不夠結構化,不方便直接翻譯為MapReduce程序,AST Tree轉化為QueryBlock就是將SQL進一部抽象和結構化。
QueryBlock
QueryBlock是一條SQL最基本的組成單元,包括三個部分:輸入源,計算過程,輸出。簡單來講一個QueryBlock就是一個子查詢。
下圖為Hive中QueryBlock相關對象的類圖,解釋圖中幾個重要的屬性
- QB#aliasToSubq(表示QB類的aliasToSubq屬性)保存子查詢的QB對象,aliasToSubq key值是子查詢的別名
- QB#qbp 即QBParseInfo保存一個基本SQL單元中的給個操作部分的AST Tree結構,QBParseInfo#nameToDest這個HashMap保存查詢單元的輸出,key的形式是inclause-i(由于Hive 支持Multi Insert語句,所以可能有多個輸出),value是對應的ASTNode節點,即TOK_DESTINATION節點。類QBParseInfo其余 HashMap屬性分別保存輸出和各個操作的ASTNode節點的對應關系。
- QBParseInfo#JoinExpr保存TOK_JOIN節點。QB#QBJoinTree是對Join語法樹的結構化。
- QB#qbm保存每個輸入表的元信息,比如表在HDFS上的路徑,保存表數據的文件格式等。
- QBExpr這個對象是為了表示Union操作。
AST Tree生成QueryBlock
AST Tree生成QueryBlock的過程是一個遞歸的過程,先序遍歷AST Tree,遇到不同的Token節點,保存到相應的屬性中,主要包含以下幾個過程
- TOK_QUERY => 創建QB對象,循環遞歸子節點
- TOK_FROM => 將表名語法部分保存到QB對象的
TOK_INSERT => 循環遞歸子節點
TOK_DESTINATION => 將輸出目標的語法部分保存在QBParseInfo對象的nameToDest屬性中
TOK_SELECT => 分別將查詢表達式的語法部分保存在
destToAggregationExprs
、TOK_WHERE => 將Where部分的語法保存在QBParseInfo對象的destToWhereExpr屬性中
最終樣例SQL生成兩個QB對象,QB對象的關系如下,QB1是外層查詢,QB2是子查詢
QB1 \ QB2
Phase3 - 邏輯操作符Operator
Operator
Hive最終生成的MapReduce任務,Map階段和Reduce階段均由OperatorTree組成。邏輯操作符,就是在Map階段或者Reduce階段完成單一特定的操作。
基本的操作符包括TableScanOperator,SelectOperator,FilterOperator,JoinOperator,GroupByOperator,ReduceSinkOperator
從名字就能猜出各個操作符完成的功能,TableScanOperator從MapReduce框架的Map接口原始輸入表的數據,控制掃描表的數據行數,標記是從原表中取數據。JoinOperator完成Join操作。FilterOperator完成過濾操作
ReduceSinkOperator將Map端的字段組合序列化為Reduce Key/value, Partition Key,只可能出現在Map階段,同時也標志著Hive生成的MapReduce程序中Map階段的結束。
Phase4 - 邏輯層優化器
大部分邏輯層優化器通過變換OperatorTree,合并操作符,達到減少MapReduce Job,減少shuffle數據量的目的。
②?MapJoinProcessor
② GroupByOptimizer
① PredicatePushDown
ColumnPruner
名稱 | 作用 |
---|---|
②?SimpleFetchOptimizer | 優化沒有GroupBy表達式的聚合查詢 |
MapJoin,需要SQL中提供hint,0.11版本已不用 | |
②?BucketMapJoinOptimizer | BucketMapJoin |
Map端聚合 | |
①?ReduceSinkDeDuplication | 合并線性的OperatorTree中partition/sort key相同的reduce |
謂詞前置 | |
①?CorrelationOptimizer | 利用查詢中的相關性,合并有相關性的Job,HIVE-2206 |
字段剪枝 |
表格中①的優化器均是一個Job干盡可能多的事情/合并。②的都是減少shuffle數據量,甚至不做Reduce。
CorrelationOptimizer優化器非常復雜,都能利用查詢中的相關性,合并有相關性的Job,參考?Hive Correlation Optimizer
對于樣例SQL,有兩個優化器對其進行優化。下面分別介紹這兩個優化器的作用,并補充一個優化器ReduceSinkDeDuplication的作用.
Phase5 - ?OperatorTree生成MapReduce Job的過程
OperatorTree轉化為MapReduce Job的過程分為下面幾個階段
- 對輸出表生成MoveTask
- 從OperatorTree的其中一個根節點向下深度優先遍歷
- ReduceSinkOperator標示Map/Reduce的界限,多個Job間的界限
- 遍歷其他根節點,遇過碰到JoinOperator合并MapReduceTask
- 生成StatTask更新元數據
- 剪斷Map與Reduce間的Operator的關系
Phase6 - 物理層優化器
這里不詳細介紹每個優化器的原理,單獨介紹一下MapJoin的優化器
SortMergeJoinResolver
CommonJoinResolver + MapJoinResolver
名稱 | 作用 |
---|---|
Vectorizer | HIVE-4160,將在0.13中發布 |
與bucket配合,類似于歸并排序 | |
SamplingOptimizer | 并行order by優化器,在0.12中發布 |
MapJoin優化器 |
MapJoin原理
MapJoin簡單說就是在Map階段將小表讀入內存,順序掃描大表完成Join。
上圖是Hive MapJoin的原理圖,出自Facebook工程師Liyin Tang的一篇介紹Join優化的slice,從圖中可以看出MapJoin分為兩個階段:
-
通過MapReduce Local Task,將小表讀入內存,生成HashTableFiles上傳至Distributed Cache中,這里會對HashTableFiles進行壓縮。
-
MapReduce Job在Map階段,每個Mapper從Distributed Cache讀取HashTableFiles到內存中,順序掃描大表,在Map階段直接進行Join,將數據傳遞給下一個MapReduce任務。
如果Join的兩張表一張表是臨時表,就會生成一個ConditionalTask,在運行期間判斷是否使用MapJoin
CommonJoinResolver優化器
CommonJoinResolver優化器就是將CommonJoin轉化為MapJoin,轉化過程如下
- 深度優先遍歷Task Tree
- 找到JoinOperator,判斷左右表數據量大小
- 對與小表 + 大表 => MapJoinTask,對于小/大表 + 中間表 => ConditionalTask
遍歷上一個階段生成的MapReduce任務,發現JOIN[8]
中有一張表為臨時表,先對Stage-2進行深度拷貝(由于需要保留原始執行計劃為Backup
Plan,所以這里將執行計劃拷貝了一份),生成一個MapJoinOperator替代JoinOperator,然后生成一個MapReduceLocalWork讀取小表生成HashTableFiles上傳至DistributedCache中。
Operator在Map Reduce階段之間的數據傳遞都是一個流式的過程。每一個Operator對一行數據完成操作后之后將數據傳遞給childOperator計算。
Operator類的主要屬性和方法如下
- RowSchema表示Operator的輸出字段
- InputObjInspector outputObjInspector解析輸入和輸出字段
- processOp接收父Operator傳遞的數據,forward將處理好的數據傳遞給子Operator處理
- Hive每一行數據經過一個Operator處理之后,會對字段重新編號,colExprMap記錄每個表達式經過當前Operator處理前后的名稱對應關系,在下一個階段邏輯優化階段用來回溯字段名
- 由 于Hive的MapReduce程序是一個動態的程序,即不確定一個MapReduce Job會進行什么運算,可能是Join,也可能是GroupBy,所以Operator將所有運行時需要的參數保存在OperatorDesc 中,OperatorDesc在提交任務前序列化到HDFS上,在MapReduce任務執行前從HDFS讀取并反序列化。Map階段 OperatorTree在HDFS上的位置在Job.getConf(“hive.exec.plan”)
+ “/map.xml” -
QueryBlock生成Operator Tree
QueryBlock生成Operator Tree就是遍歷上一個過程中生成的QB和QBParseInfo對象的保存語法的屬性,包含如下幾個步驟:
- QB#aliasToSubq => 有子查詢,遞歸調用
- QB#aliasToTabs => TableScanOperator
- QBParseInfo#joinExpr => QBJoinTree => ReduceSinkOperator + JoinOperator
- QBParseInfo#destToWhereExpr => FilterOperator
- QBParseInfo#destToGroupby => ReduceSinkOperator + GroupByOperator
- QBParseInfo#destToOrderby => ReduceSinkOperator + ExtractOperator
由于Join/GroupBy/OrderBy均需要在Reduce階段完成,所以在生成相應操作的Operator之前都會先生成一個ReduceSinkOperator,將字段組合并序列化為Reduce Key/value, Partition Key
接下來詳細分析樣例SQL生成OperatorTree的過程
先序遍歷上一個階段生成的QB對象