1. 程式人生 > >大資料Hive系列之Hive效能優化

大資料Hive系列之Hive效能優化

一、介紹

首先,我們來看看Hadoop的計算框架特性,在此特性下會衍生哪些問題?

  1. 資料量大不是問題,資料傾斜是個問題。
  2. jobs數比較多的作業執行效率相對比較低,比如即使有幾百行的表,如果多次關聯多次彙總,產生十幾個jobs,耗時很長。原因是map reduce作業初始化的時間是比較長的。
  3. sum,count,max,min等UDAF,不怕資料傾斜問題,hadoop在map端的彙總合併優化,使資料傾斜不成問題。
  4. count(distinct ),在資料量大的情況下,效率較低,如果是多count(distinct )效率更低,因為count(distinct)是按group by 欄位分組,按distinct欄位排序,一般這種分佈方式是很傾斜的。舉個例子:比如男uv,女uv,像淘寶一天30億的pv,如果按性別分組,分配2個reduce,每個reduce處理15億資料。

面對這些問題,我們能有哪些有效的優化手段呢?下面列出一些在工作有效可行的優化手段:

  1. 好的模型設計事半功倍。
  2. 解決資料傾斜問題。
  3. 減少job數。
  4. 設定合理的map reduce的task數,能有效提升效能。(比如,10w+級別的計算,用160個reduce,那是相當的浪費,1個足夠)。
  5. 瞭解資料分佈,自己動手解決資料傾斜問題是個不錯的選擇。set hive.groupby.skewindata=true;這是通用的演算法優化,但演算法優化有時不能適應特定業務背景,開發人員瞭解業務,瞭解資料,可以通過業務邏輯精確有效的解決資料傾斜問題。
  6. 資料量較大的情況下,慎用count(distinct),count(distinct)容易產生傾斜問題。
  7. 對小檔案進行合併,是行至有效的提高排程效率的方法,假如所有的作業設定合理的檔案數,對雲梯的整體排程效率也會產生積極的正向影響。
  8. 優化時把握整體,單個作業最優不如整體最優。

而接下來,我們心中應該會有一些疑問,影響效能的根源是什麼?

二、效能低下的根源

  • hive效能優化時,把HiveQL當做M/R程式來讀,即從M/R的執行角度來考慮優化效能,從更底層思考如何優化運算效能,而不僅僅侷限於邏輯程式碼的替換層面。
  • RAC(Real Application Cluster)真正應用叢集就像一輛機動靈活的小貨車,響應快;Hadoop就像吞吐量巨大的輪船,啟動開銷大,如果每次只做小數量的輸入輸出,利用率將會很低。所以用好Hadoop的首要任務是增大每次任務所搭載的資料量。
  • Hadoop的核心能力是parition和sort,因而這也是優化的根本。
  • 觀察Hadoop處理資料的過程,有幾個顯著的特徵:
  1. 資料的大規模並不是負載重點,造成執行壓力過大是因為執行資料的傾斜。
  2. jobs數比較多的作業執行效率相對比較低,比如即使有幾百行的表,如果多次關聯對此彙總,產生幾十個jobs,將會需要30分鐘以上的時間且大部分時間被用於作業分配,初始化和資料輸出。M/R作業初始化的時間是比較耗時間資源的一個部分。
  3. 在使用SUM,COUNT,MAX,MIN等UDAF函式時,不怕資料傾斜問題,Hadoop在Map端的彙總合併優化過,使資料傾斜不成問題。
  4. COUNT(DISTINCT)在資料量大的情況下,效率較低,如果多COUNT(DISTINCT)效率更低,因為COUNT(DISTINCT)是按GROUP BY欄位分組,按DISTINCT欄位排序,一般這種分散式方式是很傾斜的;比如:男UV,女UV,淘寶一天30億的PV,如果按性別分組,分配2個reduce,每個reduce處理15億資料。
  5. 資料傾斜是導致效率大幅降低的主要原因,可以採用多一次 Map/Reduce 的方法, 避免傾斜。
  • 最後得出的結論是:避實就虛,用 job 數的增加,輸入量的增加,佔用更多儲存空間,充分利用空閒 CPU 等各種方法,分解資料傾斜造成的負擔。

三、配置角度優化

我們知道了效能低下的根源,同樣,我們也可以從Hive的配置解讀去優化。Hive系統內部已針對不同的查詢預設定了優化方法,使用者可以通過調整配置進行控制, 以下舉例介紹部分優化的策略以及優化控制選項。

1.  列裁剪

Hive 在讀資料的時候,可以只讀取查詢中所需要用到的列,而忽略其它列。 例如,若有以下查詢:

SELECT a,b FROM q WHERE e<10;
  • 在實施此項查詢中,Q 表有 5 列(a,b,c,d,e),Hive 只讀取查詢邏輯中真實需要 的 3 列 a、b、e,而忽略列 c,d;這樣做節省了讀取開銷,中間表儲存開銷和資料整合開銷。
  • 裁剪所對應的引數項為:hive.optimize.cp=true(預設值為真)

2.  分割槽裁剪

可以在查詢的過程中減少不必要的分割槽。 例如,若有以下查詢:

SELECT * FROM (SELECTT a1,COUNT(1) FROM T GROUP BY a1) subq WHERE subq.prtn=100; 
-- 多餘分割槽
SELECT * FROM T1 JOIN (SELECT * FROM T2) subq ON (T1.a1=subq.a2) WHERE subq.prtn=100;
  • 查詢語句若將“subq.prtn=100”條件放入子查詢中更為高效,可以減少讀入的分割槽 數目。 Hive 自動執行這種裁剪優化。
  • 分割槽引數為:hive.optimize.pruner=true(預設值為真)

3.  JOIN操作

在編寫帶有 join 操作的程式碼語句時,應該將條目少的表/子查詢放在 Join 操作符的左邊。 因為在 Reduce 階段,位於 Join 操作符左邊的表的內容會被載入進記憶體,載入條目較少的表 可以有效減少 OOM(out of memory)即記憶體溢位。所以對於同一個 key 來說,對應的 value 值小的放前,大的放後,這便是“小表放前”原則。 若一條語句中有多個 Join,依據 Join 的條件相同與否,有不同的處理方法。

3.1  JOIN原則

在使用寫有 Join 操作的查詢語句時有一條原則:應該將條目少的表/子查詢放在 Join 操作符的左邊。原因是在 Join 操作的 Reduce 階段,位於 Join 操作符左邊的表的內容會被載入進記憶體,將條目少的表放在左邊,可以有效減少發生 OOM 錯誤的機率。對於一條語句中有多個 Join 的情況,如果 Join 的條件相同,比如查詢:

INSERT OVERWRITE TABLE pv_users
SELECT pv.pageid, u.age FROM page_view p
JOIN user u ON (pv.userid = u.userid)
JOIN newuser x ON (u.userid = x.userid);
  • 如果 Join 的 key 相同,不管有多少個表,都會則會合併為一個 Map-Reduce
  • 一個 Map-Reduce 任務,而不是 ‘n’ 個
  • 在做 OUTER JOIN 的時候也是一樣

如果 Join 的條件不相同,比如:

INSERT OVERWRITE TABLE pv_users
SELECT pv.pageid, u.age FROM page_view p
JOIN user u ON (pv.userid = u.userid)
JOIN newuser x on (u.age = x.age);

Map-Reduce 的任務數目和 Join 操作的數目是對應的,上述查詢和以下查詢是等價的:

INSERT OVERWRITE TABLE tmptable
SELECT * FROM page_view p JOIN user u
ON (pv.userid = u.userid);
INSERT OVERWRITE TABLE pv_users
SELECT x.pageid, x.age FROM tmptable x
JOIN newuser y ON (x.age = y.age);

4.  MAP JOIN操作

Join 操作在 Map 階段完成,不再需要Reduce,前提條件是需要的資料在 Map 的過程中可以訪問到。比如查詢:

INSERT OVERWRITE TABLE pv_users
SELECT /*+ MAPJOIN(pv) */ pv.pageid, u.age
FROM page_view pv
JOIN user u ON (pv.userid = u.userid);
  • 可以在 Map 階段完成 Join.
  • 相關的引數為:
hive.join.emit.interval = 1000
hive.mapjoin.size.key = 10000
hive.mapjoin.cache.numrows = 10000

5.  GROUP BY操作

進行GROUP BY操作時需要注意一下幾點:

5.1  Map端部分聚合

事實上並不是所有的聚合操作都需要在reduce部分進行,很多聚合操作都可以先在Map端進行部分聚合,然後reduce端得出最終結果。

  • 這裡需要修改的引數為:
# 用於設定是否在 map 端進行聚合,預設值為真
hive.map.aggr=true

# 用於設定 map 端進行聚合操作的條目數
hive.groupby.mapaggr.checkinterval=100000

5.2  有資料傾斜時進行負載均衡

此處需要設定 hive.groupby.skewindata,當選項設定為 true 是,生成的查詢計劃有兩 個 MapReduce 任務。在第一個 MapReduce 中,map 的輸出結果集合會隨機分佈到 reduce 中, 每個 reduce 做部分聚合操作,並輸出結果。這樣處理的結果是,相同的 Group By Key 有可 能分發到不同的 reduce 中,從而達到負載均衡的目的;第二個 MapReduce 任務再根據預處 理的資料結果按照 Group By Key 分佈到 reduce 中(這個過程可以保證相同的 Group By Key 分佈到同一個 reduce 中),最後完成最終的聚合操作。

6.  合併小檔案

我們知道檔案數目小,容易在檔案儲存端造成瓶頸,給 HDFS 帶來壓力,影響處理效率。對此,可以通過合併Map和Reduce的結果檔案來消除這樣的影響。

  • 用於設定合併屬性的引數有:
# 是否合併Map輸出檔案,預設值為真
hive.merge.mapfiles=true

# 是否合併Reduce 端輸出檔案,預設值為假
hive.merge.mapredfiles=false

# 合併檔案的大小,預設值為 256000000
hive.merge.size.per.task=256*1000*1000