天天看點

《Hive程式設計指南》讀書筆記

作者:京東雲開發者

前言:

最近剛接觸寫Hive SQL,卻發現許多查詢的執行速度遠不如預期。為了提升查詢效率,我去閱讀了《Hive程式設計指南》,希望通過了解其底層機制來找到優化的方式,并為未來能編寫出高效的SQL奠定基礎。謹以此文做個記錄。

一、Hive因何而生

先有Hadoop再有Hive

Hadoop實作了一個計算模型——MapReduce,它可以将計算任務分割成多個處理單元然後分散到一群家用的或伺服器級别的硬體機器上,進而降低計算成本并提供水準可伸縮性。但是這套程式設計模型對于大多數資料分析分析師較為複雜和地銷,即便是Java開發編寫MapReduce程式也需要很多時間和精力。基于此,Hive提供了基于SQL的查詢語言(HiveQL),這邊能夠讓擁有SQL知識的使用者能夠輕松使用Hadoop進行大資料分析,因為Hive的底層會自動将這些查詢轉換為MapReduce任務。

二、Hive組成子產品

《Hive程式設計指南》讀書筆記

所有的指令和查詢都會進入Driver,通過該子產品對輸入進行解析編譯,對需求的計算進行優化,然後按照指定的步驟執行。

Hive通過JobTracker通信來初始化MapReduce任務,需要處理的資料檔案是存儲在HDFS中的,而HDFS是由NameNode進行管理的。

Metastore(中繼資料存儲)是一個獨立的關系型資料庫,Hive會在其中儲存表模式和其他系統中繼資料。

三、HQL執行流程

《Hive程式設計指南》讀書筆記

簡單來說,Hive會從Hadoop分布式檔案系統(HDFS)中讀取原始資料,然後根據查詢定義,在單節點(本地模式)或者Hadoop叢集上(叢集模式)執行資料處理。處理完成後,Hive會将結果輸出到HDFS或者其他指定的存儲位置。

那麼,Hive的執行時間主要花費在哪兒呢?我們可優化的部分是哪部分?

Hive的執行時間主要花費在以下幾個階段:

1.查詢編譯:Hive 将 HiveQL 查詢編譯成一個邏輯執行計劃,這個計劃描述了如何執行查詢。此階段包括文法分析、語義分析、生成邏輯計劃、邏輯計劃優化和生成實體計劃(通常是 MapReduce 作業)。

2.任務排程:編譯生成的 MapReduce 作業被送出到 Hadoop 叢集的資料總管(如 YARN),等待資源排程和作業執行。

3.資料讀寫:讀取存儲在 HDFS 上的資料以及寫入最終結果到 HDFS,這個過程涉及大量的磁盤 I/O 操作,尤其是在處理大量資料集時。

4.MapReduce 作業執行:包括

1.Map 階段:執行過濾、投影等操作;

2.Shuffle 階段:Map 任務輸出的中間資料在網絡上傳輸并在 Reduce 節點上進行排序和合并;

3.Reduce 階段:執行聚合、排序等操作;

5.網絡傳輸:在 MapReduce 的 Shuffle 階段,中間資料需要在叢集節點之間傳輸,這可能導緻顯著的網絡延遲。

通常,MapReduce 作業的執行時間(尤其是 Shuffle 和 Reduce 階段)以及資料的讀寫操作是 Hive 查詢中最耗時的部分,也是我們優化過程中主要關注的部分,接下來我們看下有哪些常見的優化方式。

四、Hive常見的優化方式

本地模式

-- 開啟本地模式,預設為false
hive.exec.mode.local.auto=true

原理:有時Hive的輸入資料量是非常小的。在這種情況下,為查詢觸發執行任務的時間消耗可能會比實際job的執行時間要多得多。對于大多數這種情況,Hive可以通過本地模式在單台機器上處理所有的任務。對于小資料集,執行時間可以明顯被縮短。使用者可以通過設計屬性
hive.exec.mode.local.auto的值為true,來讓Hive在适當的時候自動啟動這個優化。實踐有效,但如果并行執行的SQL過多,容易造成本地記憶體溢出。

           

map-side JOIN優化

#-- Hive v0.7之前需要通過添加标記 /*+ MAPJOIN(X) */ 觸發,如下圖           
《Hive程式設計指南》讀書筆記
-- Hive v0.7版本開始之後,通過設定hive.auto.convert.JOIN的值為true開啟
set hive.auto.convert.JOIN=true
-- 設定小表的大下,機關為位元組
set hive.mapjoin.smalltable.filesize=25000000

原理:如果所有表中有一個表足夠得小,是可以完成載入記憶體中的,那麼這時Hive可以執行一個map-side JOIN,将小表完全放到記憶體,Hive便可以直接和記憶體中的小表進行逐一比對,進而減少所需要的reduce過程,有時甚至可以減少某些map task任務。

           

并發執行

-- 通過設定參數hive.exec.parallel值為true,開啟并發執行,預設為false
set hive.exec.parallel=true

原理:Hive會将一個查詢轉化成一個或者多個階段。這樣的階段可以是MapReduce階段、抽樣階段、合并階段、limit階段等。預設情況下,Hive一次隻會執行一個階段。但是有些階段并非完全互相依賴的,也就是說這些階段是可以并行執行的,這樣可以使得整個job的執行時間縮短。
通過設定參數hive.exec.parallel值為true,就可以開啟并發執行。 

           

動态分區調整

-- 啟用動态分區,預設為false;
SET hive.exec.dynamic.partition=true;
-- 啟用動态分區模式為非嚴格模式。開啟嚴格模式時們必須保證至少有一個分區時靜态的。
SET hive.exec.dynamic.partition.mode=nonstrict;
-- 設定在一個動态分區插入操作中可以建立的最大分區數量
SET hive.exec.max.dynamic.partitions=1000;
-- 設定每個節點可以建立的最大分區數量
SET hive.exec.max.dynamic.partitions.pernode=100;

當執行查詢時,如果查詢條件包含分區鍵,Hive可以僅掃描相關分區的資料,進而減少了掃描的資料量,提高查詢效率;在執行動态分區的插入時,這些分區也可以并行寫入,進而提高了資料寫入的并行度和性能。通過以上參數,可更好的使用動态分區。

           

合并小檔案

--是否和并Map輸出檔案,預設true
SET hive.merge.mapfiles=true;
--是否合并 Reduce 輸出檔案,預設false
SET hive.merge.mapredfiles=true;
-- 設定合并檔案的大小門檻值
SET hive.merge.size.per.task=256000000; 
-- 設定小檔案的平均大小門檻值
SET hive.merge.smallfiles.avgsize=128000000; 

由于一些小批量的寫入、MapReduce作業切割、資料傾斜等原因,Hive中可能會産生大量小檔案,通過以上參數可進行小檔案合并以減少讀取檔案時的開銷、降低NameNode壓力,提升查詢效率。

           

資料傾斜優化

資料傾斜指的是在分布式處理過程中,資料不均勻地配置設定給各個節點處理,導緻部分節點負載過重,而其他節點負載輕松,進而影響整體計算效率。資料傾斜出現的原因主要如下:

1.鍵值分布不均勻:有些鍵值對應的資料遠多于其他鍵值;

2.量相同鍵值:大量資料使用相同的鍵(如null或者特定的預設值)進行分組;

3.不合理的JOIN操作:在JOIN大表時,如果小表的某個鍵值在大表中分布不均,導緻JOIN後的結果傾斜;

4.不合理的分區政策:資料分區時沒有考慮資料的實際分布,導緻分區不均勻。

主要解決方案有:

1.自定義分區政策:實作自定義分區期,根據資料的特點進行更合理的分區;

2.擴充鍵值:對傾斜的鍵添加随機字首或編号,使其分散到多個分區;

3.過濾大鍵值資料:識别出傾斜的鍵值(如null、空值)進行單獨處理或過濾掉不重要的資料。

最後就是我們關系型資料庫常用的優化方式同樣也适用與Hive。例如通過使用小表關聯大表的方式減少查詢資料量,提高查詢效率;Hive同樣也有索引的概念,通過建立索引減少MapReduce的輸入資料量,但同樣和關系型資料庫一樣,是否使用索引需要進行仔細評估,因為維護索引也需要額外的存儲空間,而且建立索引同樣消耗計算資源;Hive同樣也有EXPLAIN關鍵字,用于查詢Hive時如何将查詢轉化為MapReduce任務的,使用EXPLAIN EXTENDED語句可以産生更多的輸出資訊,有興趣大家可自行檢視。

總體而言,這本書對于剛入門學習寫HQL的我來說收獲很大,讓我初步對Hive有了基本的認知,也讓我對我寫的SQL有了更深入的了解。但是該書中的Hive應該版本比較低了,和我們現在所使用的可能有所偏差,不過入個門足夠了。本文除了書中内容還有些我個人了解,如有錯誤,歡迎指正。

作者: 馬壯

來源:京東雲開發者社群 轉載請注明來源

繼續閱讀