hive 總體架構解析
最近整理了下自己以前的筆記,對Hive整體執行流程做了一些總結。
一、Hive簡介和原理
1)Hive起源
Hive起源於FACEBOOK,由FACEBOOK的幾名員工共同提出。最早提出的Hive概念來源於以下論文Hive——A Warehousing Solution Over A Map-Reduce Framework(論文見目錄)。它是基於MapReduce框架的資料倉庫的解決方案。主要解決了並行執行資料分析和機器查詢的效率。Hive已經成為最流行的基於Hadoop的資料儲存框架和資料分析框架,以及事實的大資料SQL標準和元資料標準;是Hadoop生態棧中最主要的基礎架構。
Hive主要是面向非專業人士,尤其是大資料分析師最好的工具。Hive的提供的功能非常健全,是效率最高、便於使用的作業提交工具。
簡化後的HIVE架構:
2)HIVE工作流
HIVE的最小處理單元是OPERATORS ,OPERATORS(該術語來源於關係型資料中的術語,操作符。)可以是執行在叢集的MAP任務,也可以是執行在叢集的REDUCE任務,或者是執行在本地對於HDFS的操作;這些都會被抽象成OPERATORS。
HIVE的COMPILER的作用是:把一條HIVE SQL,轉換為一個OPERATORS的 圖(這裡大部分情況下會是一個樹,樹狀結構)。
HIVE中OPERATORS的種類:
OPERATORS |
FUNCTION DESCRIBLE |
縮寫 |
TableScanOperator |
從表中讀取資料 |
TS |
ReduceSinkOperator |
把資料傳送給Reduce端作聚合操作 |
RS |
JoinOperator |
作兩個表的關聯操作Join |
JOIN |
SelectOperator(投影) |
選擇部分列並輸出 |
SEL |
FileSinkOperator |
建立結果資料併發送給檔案 |
FS |
FilterOperator |
過濾輸入資料 |
FIL |
GroupByOperator |
對資料作Group By操作 |
GBY |
MapJoinOperator |
在記憶體中作大表和小表的關聯操作 |
MapJOIN |
LimitOperator |
作limit,返回一定條目的資料 |
LIMIT |
UnionOperator |
作union |
UNION |
在較老的版本中,HIVE通常是一個MAPREDUCE作業(新版本可能是TEZ)。MAPPER是EXECMAPPER,REDUCER是EXECREDUCE。DRICVER實際上有多種處理模式,例如本地模式和分散式模式。
在HIVE中最核心的模組就是COMPILER,它負責將一個字串(HIVESQL),轉化成一個執行計劃。
簡化後的執行流程圖:
為了說明上述執行流程,需要輔助一個簡單的Hive查詢例子:
案例1-1:Hive執行以下語句:
INSERT OVERWRITE TABLE access_log_temp2
SELECT a.user, a.prono, a.maker, a.price FROM access_log_hbase a JOIN product_hbase p
ON (a.prono = p.prono)
3)具體執行流程:
(1)Parser生成AST
HQL生成的語法解析樹(AST):
HIVE主要是利用UNDERLER處理得到上面的抽象語法樹(這棵抽象語法樹主要是根據underller的分詞規則來確定根結點和左右孩子的,Hive的這部分處理是利用underller進行的)。
(2)Semantic Analyzer生成Query Block(QB)
Tip:
第二步會將抽象語法樹按照QB的方式轉換為由QB組成的樹。通常情況下,每一個From子句會生成一個QB(Query Block)。通過檢視具體有多少個QB就可檢視Hive sql語句最終優化成多少個MapReduce的作業。
QB是由兩個子資料結構組成即MetaData(元資料資訊)和ParseInfo。語義分析的主要過程是向QB中填充元資料,另一個過程則是摘取AST的子節點,儲存在ParseInfo中。Semantic Analyzer過程主要是經過以上兩個過程,然後把QB關聯起來形成一個由QB構成的圖.
Logical Plan generator會將上一步生成的Query Block按照前文講的幾種的Operator,轉換為Operator Tree。
由於Hive查詢語句會將最後的查詢結果寫入到表access_log_temp2中,所以QB的MetaData中的Name To Destination…會轉化為Operator Tree中的FileSinkOperator(檔案下沉)。
最後會生成如下的OP Tree:
Logical Plan Generator (Result)
這裡就得到了一個有向無環圖(DAG),當在傳統資料庫中會對上圖進行優化然後執行;而在Hadoop叢集中這張圖是不行的,因為在叢集中執行需要對這張圖進行優化、切片,分散式化轉化為MapReduce作業然後執行。
(4)LOGICAL OPTIMIZER
首先,Hive會對上圖進行優化,重繪。
這裡需要介紹Hive中的優化器如下表:
優化器 |
簡介 |
LineageGenerator |
各個Operator血緣情況的設定,優化Operator的血緣關係 |
ColumnPruner |
列剪裁優化器 |
Predicate PushDown |
謂詞下推優化器,將條件推到特定位置(where條件) |
PartitionPruner |
分割槽裁剪條件優化器 |
PartitionCondition Remove |
PartitionPruner消除無用的分支的優化器 |
GroupByOptimizer |
Group by優化兩階段中的Map端預階段聚合的優化器 |
SamplePruner |
抽樣優化器,降低抽樣的資料量 |
MapJoinProcessor |
在特定的情況下,把JoinOperator改寫成MapJoinOperator的 優化器 |
BucketMapJoin Optimizer |
對Bucket表做MapJoin的優化器 |
SortedMergeBucket MapJoinOptimizer |
對SortedMergeBucket進行MapJoin的優化器 |
優化器 |
簡介 |
UnionProcessor |
識別兩邊的子查詢是否都是Map-Only的 |
JoinReader |
/*+ STREAMTABLE(A) */ 指定Join的 |
ReduceSink DeDuplication |
如果兩個ReduceSink的操作符共享分割槽和排列順序也一樣,此時就可以將這兩個表放在一起進行ReduceSink操作,提高效率。 |
上表中的優化器都是邏輯優化器。為了進一步介紹這些優化器,這裡舉個例子來進行說明。
案例1-2 LOGICALOPTIMIZER(PREDIC PUSHDOWN)
INSERTOVERWRITE TABLE access_log_temp2
SELECTa.user, a.prono, a.maker, a.price FROM access_log_hbase a JOIN product_hbase p
ON(a.prono = p.prono) where p.mark = “honda”;
此時,原來的邏輯計劃圖就會增加一個節點,如下圖:
這是一個未進行優化的OP Tree,此時會存在一個問題:當access_log_hbase和product_hbase兩個表的資料量非常大的時候,第三步進行的Join操作,就會產生海量的數之前就先進性過濾出”honda”的資料,此時向Reduce端傳送的資料會變得很少,Join時資料也會變得很少,從而大幅度提高Hive SQL的執行效率。這一個優化的過程就叫做謂詞下推,即把where選擇過濾操作下推到合適的步驟進行。
進過謂詞下推優化器優化後的OP Tree如下圖:
(5)PHYSICAL PLAN GENERATOR
物理執行計劃會將OP Tree切成若干個Task,而一個Task就對應著一個MapReduce作業。由於案例中只有一個QB,所以最後會生成一個Task即一個MapReduce作業。
當DAG中存在Join或者Group By時,會在這個Operator之前的節點操作會在Map中執行,而這個Operator之後的節點都會在Reduce執行。而兩者會通過ReduceSinkOperator操作來連線,這個操作符會將Map端的資料傳送到Reduce端。
PHYSICAL PLAN GENERATOR (Result)
這樣就將一個OP Tree轉換為了一個Map-Red的作業。但是這個Map-Red作業還需要進行物理級別的優化。
(6)PHYSICAL OPTIMIZER
物理優化器
物理優化的類主要在Hive的org/apache/hadoop/hive/ql/optimizer/physical/包內。
主要有以下幾個物理優化器。
i)以MapJoinResolver為例,介紹物理優化
請看下圖(前例優化後的物理執行計劃)
1.此時,只有Map過程,即在本地進行小表的壓縮、上傳;在服務端讀大表然後進行關聯。
2.在進行Join時,一般使用者都會使用MapJoin來手動指令Select Map Join來進行操作。但是,如果用在進行Join時,使用者並不知道哪個表大哪個表小也不知道該不該使用Map Join,此時使用CommonJoinResolver則會解決這一問題。首先不管表大還是表小,他都會對Task Tree做一定的優化,會在Join 的Map-Red Task之前加上一個conditional Task。當作業到執行到Conditional Task時,會在執行Join的Map-Red作業之前對錶的大小先進行判斷;如果表是大表則進行Join操作;當表是小表時則進行MapJoin操作。這樣使用者就不必關心表大還是小,也不需要關心具體執行過程。
這裡把原來的物理計劃中Join換成了MapJoin,從而避免了Map-Red過程,轉換成為了只有Map過程的Task。從而避免了資料在各個節點之間的傳輸,大幅度提高了Task的執行效率。但是,這個物理執行計劃的DAG是不能直接執行的,這裡無法確定那個是大表,從而無法確定那個表要讀入到記憶體。而物理優化器的作用就是對一個Task Tree(DAG)進行優化,選出讀入記憶體的表,進行合理的優化提高執行效率。
具體轉換過程見下圖:
轉換後Task Tree是由兩個Task組成的DAG。第一個Task是在本地進行MapredLocalTask,首先會在本地讀小表,然後把小表通過HashTableSinkOperator轉換為一個Hashtable,然後打包、壓縮,通過DistrubuteChach機制將其上傳到伺服器上。
在伺服器上會先去讀取大表,然後MapJoinOperator會去下載小表,然後將其讀入到記憶體中,然後再記憶體中進行MapJoin。由於Map Join是在記憶體中進行的,並且此時只是均勻的去讀一個大表,每個程序只讀64M,可以在一個均勻的時間內完成Select並輸出。此時只需要客戶端都一個小表,伺服器都一個大表完成MapJoin操作。
物理優化器的作用主要是改寫Task Tree,根據不同的情況有不同的給些方式。詳細內容請檢視物理優化部分。
最後,來整體做個總結。
Hive 整體架構:
執行流程回顧:
補充:如何檢視執行計劃?
只需要在Hive中執行的SQL語句前面加上EXPLAN語句。
**tip**
這裡的總結都是我參加hive2.1培訓的筆記(小象學院)。
雖然大體上對整個hive的執行流程做了個總結吧。但是對於初學者還是很難全部理解的,後面我會整理一些更細節的hive各個模組的具體理解內容。