無論你通過哪種方式連線Hive(如Hive Cli、HiveServer2),一個HQL語句都要經過Driver的解析和執行,主要涉及HQL解析、編譯、優化器處理、執行器執行四個方面。
以Hive目前原生支援計算引擎MapReduce為例,具體處理流程如下:
- HQL解析生成AST語法樹Antlr定義SQL的語法規則,完成SQL詞法和語法解析,將SQL轉化為抽象語法樹AST Tree
- 語法分析得到QueryBlock遍歷AST Tree,抽象出查詢的基本組成單元QueryBlock
- 生成邏輯執行計劃遍歷QueryBlock,翻譯為執行操作樹Operator Tree
- Logical Optimizer Operator進行邏輯優化邏輯層優化器進行OperatorTree變換,合併不必要的ReduceSinkOperator,減少shuffle資料量
- 生成物理執行計劃Task Plan遍歷Operator Tree,翻譯為MapReduce任務
- 物理優化Task Tree,構建執行計劃QueryPlan物理層優化器進行MapReduce任務的變換,生成最終的執行計劃
- 表以及其他操作鑑權
- 執行引擎執行
在Hive Query整個生命週期中,會有如下鉤子函式被執行:
HiveDriverRunHook的preDriverRun
該鉤子函式由引數hive.exec.driver.run.hooks控制,決定要執行的pre hooks,多個鉤子實現類以逗號間隔,鉤子需實現 org.apache.hadoop.hive.ql.HiveDriverRunHook介面。
HiveSemanticAnalyzerHook的preAnalyze
在Driver開始run之前,HQL經過解析會進入編譯階段的語法分析,而在語法分析前會經過鉤子HiveSemanticAnalyzerHook的preAnalyze方法處理。該鉤子函式由hive.semantic.analyzer.hook配置,鉤子需實現org.apache.hadoop.hive.ql.parse.HiveSemanticAnalyzerHook介面。
HiveSemanticAnalyzerHook的postAnalyze
與preAnalyze同屬於一個鉤子類,配置引數相同,會執行所有配置的語義分析hooks,但它位於Hive的語法分析之後,可以獲取HQL的輸入和輸出表及分割槽資訊,以及語法分析得到的task資訊,由此可以判斷是否是需要分散式執行的任務,以及執行引擎是什麼。
生成執行計劃之前的redactor鉤子
該鉤子由hive.exec.query.redactor.hooks配置,多個實現類以逗號間隔,鉤子需繼承org.apache.hadoop.hive.ql.hooks.Redactor抽象類,並替換redactQuery方法。
這個鉤子函式是在語法分析之後,生成QueryPlan之前,所以執行它的時候語法分析已完成,具體要跑的任務已定,這個鉤子的目的在於完成QueryString的替換,比如QueryString中包含敏感的表或欄位資訊,在這裡都可以完成替換,從而在Yarn的RM介面或其他方式查詢該任務的時候,會顯示經過替換後的HQL。
task執行前的preExecutionHook
在執行計劃QueryPlan生成完,並通過鑑權後,就會執行具體的task,而task執行之前會經過一個鉤子函式,鉤子函式由hive.exec.pre.hooks配置,多個鉤子實現類以逗號間隔。實現方式:
1)實現org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext
通過實現該介面的run方法,執行所有的pre-execution hooks
// Pre/Post Execute Hook can run with the HookContext public interface ExecuteWithHookContext extends Hook { /** hookContext: The hook context passed to each hooks. * HookContext帶有執行計劃、Hive的配置資訊、Lineage、UGI、提交的使用者以及輸入輸出表等資訊 */ void run(HookContext hookContext) throws Exception; }
2)實現org.apache.hadoop.hive.ql.hooks.PreExecute
該介面的run方法已經標註為過時,並且相對於ExecuteWithHookContext,PreExecute提供的資訊可能不能完全滿足我們的業務需求。
public interface PreExecute extends Hook { /** * The run command that is called just before the execution of the query. * SessionState、UGI、HQL輸入表及分割槽資訊,HQL輸出表、分割槽以及本地和hdfs檔案目錄資訊 */ @Deprecated public void run(SessionState sess, Set<ReadEntity> inputs,Set<WriteEntity> outputs, UserGroupInformation ugi) throws Exception; }
task執行失敗時的ON_FAILURE_HOOKS
task執行失敗時,Hive會呼叫這個hook執行一些處理措施。該鉤子由引數hive.exec.failure.hooks配置,多個鉤子實現類以逗號間隔。需實實現org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext介面。
task執行完成時的postExecutionHook
在task任務執行完成後執行。如果task失敗,會先執行ON_FAILURE_HOOKS,之後執行postExecutionHook,該鉤子由引數hive.exec.post.hooks指定的hooks(多個鉤子實現類以逗號間隔)執行post execution hooks。實現方式:
1)實現org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext
2)實現org.apache.hadoop.hive.ql.hooks.PostExecute
ExecuteWithHookContext和PostExecute跟分別與上述task執行前的preExecutionHook、PreExecute對應,這裡不再贅述。
HiveDriverRunHook的postDriverRun
在查詢完成執行之後以及將結果返回給客戶端之前執行,與preDriverRun對應。
此外,Hive中已經有一些內建實現的hook,下面舉一些例子以及它們的主要作用:
ATSHook:實現了ExecuteWithHookContext,將查詢和計劃資訊推送到Yarn App Timeline Server。
DriverTestHook:實現了HiveDriverRunHook的preDriverRun方法(對postDriverRun是空實現),用於列印輸出的命令。
EnforceReadOnlyTables:pre execute hook,實現了ExecuteWithHookContext,用於阻止修改只讀表。
LineageLogger:實現了ExecuteWithHookContext,它將查詢的血統資訊記錄到日誌檔案中。LineageInfo包含有關query血統的所有資訊。
PreExecutePrinter和PostExecutePrinter:pre和post hook的示例,它將引數列印輸出。
PostExecTezSummaryPrinter:post execution hook,實現了ExecuteWithHookContext,可以列印Hive Tez計數器的相關資訊。
PostExecOrcFileDump:post execution hook,實現了ExecuteWithHookContext,用於列印ORC檔案資訊。
UpdateInputAccessTimeHook:pre execution hook,可在執行查詢之前更新所有輸入表的訪問時間。
特別強調一下LineageLogger和LineageInfo,對於做Hive血緣關係分析很有參考價值,當然Hive血緣分析不是本篇文章的重點,這裡先不做展開。
通過對上面Hive中hook的執行"位置"和作用,以及Hive本身實現的一些Hook,分析可知:自定義hook,比如實現一個pre execution hook。
首先在maven的pom中引入hive-exec的依賴,如:
<dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>2.1.0</version> </dependency>
此外,還需建立一個實現ExecuteWithHookContext的類,實現其中的run方法,並設定相應的引數,使自定義的hook類生效。
最後,通過一張圖,來對Hive Hook做個總結: