從客戶端提交一個 Hive SQL 到 Driver 提交 MapReduce Job,有一個對SQL進行詞法分析和語義分析的過程,下面以 select count(*) from tableName 來描述其過程。
一、詞法分析
使用ANTLR分析SQL,生成語法樹,每個節點是一個 ASTNode,它有自己的類型。
來看看 select count(*) from tableName 的語法樹:
(
TOK_QUERY
(
TOK_FROM
(
TOK_TABREF
(
TOK_TABNAME
tt2
)
)
)
(
TOK_INSERT
(
TOK_DESTINATION
(
TOK_DIR
TOK_TMP_FILE
)
)
(
TOK_SELECT
(
TOK_SELEXPR
(
TOK_FUNCTIONSTAR
count
)
)
)
)
)
上面語法樹中,除括號外,每行對應一個 ASTNode,共13個節點,相互構成父子關係。
如:TOK_QUERY和TOK_INSERT爲TOK_FROM的子節點;tt2爲TOK_TABNAME的子節點。
二、語義分析
首先根據SQL類型創建對應的SemanticAnalyzer,對於這個sql,創建的就是 SemanticAnalyzer
1.分析語法樹的每個節點,將後面要用到的相關數據保存到 Query Block
2.查詢表的元數據信息,順便做些檢查,如檢查 table 是否 offline
3.創建 Operator
4.優化
5.生成 Task
這些步驟中,除去輔助工作外,最重要的是3、5部分。
3 根據語法樹創建Operator
生成的 Operator 是個 DAG,每個節點是一個 Operator,每個 Operator 應該有指向所有子 Operator的指針,那麼Operator的數據結構至少是這個樣子:
class Operator<T extends OperatorDesc> implements Node {
public List<Operator<? extends OperatorDesc>> getParentOperators() {
return parentOperators;
}
public List<Operator<? extends OperatorDesc>> getChildOperators() {
return childOperators;
}
}
先看看 select count(*) from tableName 的Operator:
爲了生成 operator tree,首先創建 operator tree 的 root operator,而root operator 總是 TableScanOperator
這個步驟會爲所有from字句中的表創建一個 TableScanOperator,將 TableScanOperator 和表名的對應關係保存起來。
最先創建的總是 TableScanOperator,以後每創建一個Operator 都將當前Operator作爲它的父Operator
ReduceSinkOperator和它的子Operator: GroupByOperator是同時創建的,它來幫助mapper輸出結果,接着就由Reduce進行聚集操作。
所以創建Task時,如遇到處理的節點爲ReduceSinkOperator,就直接根據它的子Operator 來生成Reduce Task.
可見這個SQL 中 TableScanOperator 的子Operator 只有一個 SelectOperator;FileSinkOperator 有一個父Operator: SelectOperator
在explain 中就可查看有哪些Operator:
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-0 is a root stage
STAGE PLANS:
Stage: Stage-1
Map Reduce
Alias -> Map Operator Tree:
tt2
TableScan //[1]
alias: tt2
Select Operator //[2]
Group By Operator //[3]
aggregations:
expr: count()
bucketGroup: false
mode: hash
outputColumnNames: _col0
Reduce Output Operator //[4]
sort order:
tag: -1
value expressions:
expr: _col0
type: bigint
Reduce Operator Tree:
Group By Operator //[5]
aggregations:
expr: count(VALUE._col0)
bucketGroup: false
mode: mergepartial
outputColumnNames: _col0
Select Operator //[6]
expressions:
expr: _col0
type: bigint
outputColumnNames: _col0
File Output Operator //[7]
compressed: false
GlobalTableId: 0
table:
input format: org.apache.hadoop.mapred.TextInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
Stage: Stage-0
Fetch Operator
limit: -1
上面 explain 中的Operator 已標出。
TableScanOperator 不用說了,望文生義吧
SelectOperator 可進行列修剪,可減少 mapper 的輸出數據量。
GroupByOperator 進行聚集操作,map 端的自然只能做部分聚集了。
FileSinkOperator 向文件系統寫SQL結果文件。
5根據 Operator tree 創建 Task
首先創建一個Dispatcher,它保存了用RegExp標識的規則和相應的節點處理器NodeProcessor的映射集合,
這個集合決定了每一個節點 Operator的命運,它決定了處理它的NodeProcessor.
不同的規則的正則表達式可匹配對應的 Operator。
然後頂端(TableScanOperator)開始遍歷這個DAG,爲每個節點(Operator)找出一個規則 Rule。TableScanOperator 最匹配的規則對應的 NodeProcessor 是 GenMRTableScan1,它會創建一個 MapRedTask
SelectOperator 找到的 Processor 爲 GenMROperator,這個 Processor 僅僅保存了一下SelectOperator 和對應的 GenMRProcContext$GenMapRedCtx
的映射關係
ReduceSinkOperator 對應的 NodeProcessor: GenMRRedSink1
上面說過,以創建Task時,如遇到處理的節點爲ReduceSinkOperator,就直接根據它的子Operator 來生成Reduce Task,這就是 GenMRRedSink1 來完成的。
具體來說,GenMRRedSink1 將這個 GroupByOperator 作爲 reducer,把它存入當前任務中。當前任務就是剛纔處理TableScanOperator時創建的 Task。
所有節點處理完後會生成所有的Task,此時 PhysicalOptimizer 會再優化一次。它遍歷的其實還是個DAG,不過這時節點是 Task了。
至此,Task已經創建,可以着手創建Mapeduce Job 了。