1. 程式人生 > 實用技巧 >不可不知的十大Hive調優技巧最佳實踐

不可不知的十大Hive調優技巧最佳實踐

ApacheHive是建立在ApacheHadoop之上的資料倉庫軟體專案,用於提供資料查詢和分析。Hive是Hadoop在HDFS上的SQL介面,它提供了類似於SQL的介面來查詢儲存在與Hadoop整合的各種資料庫和檔案系統中的資料。可以說從事資料開發工作,無論是在平時的工作中,還是在面試中,Hive具有舉足輕重的地位,尤其是Hive的效能調優方面,不僅能夠在工作中提升效率而且還可以在面試中脫穎而出。在本文中,我將分享十個效能優化技術,全文如下。

1.多次INSERT單次掃描表

預設情況下,Hive會執行多次表掃描。因此,如果要在某張hive表中執行多個操作,建議使用一次掃描並使用該掃描來執行多個操作。

比如將一張表的資料多次查詢出來裝載到另外一張表中。如下面的示例,表my_table是一個分割槽表,分割槽欄位為dt,如果需要在表中查詢2個特定的分割槽日期資料,並將記錄裝載到2個不同的表中。

INSERTINTOtemp_table_20201115SELECT*FROMmy_tableWHEREdt='2020-11-15';
INSERTINTOtemp_table_20201116SELECT*FROMmy_tableWHEREdt='2020-11-16';

在以上查詢中,Hive將掃描表2次,為了避免這種情況,我們可以使用下面的方式:

FROMmy_table
INSERTINTOtemp_table_20201115SELECT*WHEREdt='2020-11-15'
INSERTINTOtemp_table_20201116SELECT*WHEREdt='2020-11-16'

這樣可以確保只對my_table表執行一次掃描,從而可以大大減少執行的時間和資源。

2.分割槽表

對於一張比較大的表,將其設計成分割槽表可以提升查詢的效能,對於一個特定分割槽的查詢,只會載入對應分割槽路徑的檔案資料,因此,當用戶使用特定分割槽列值執行選擇查詢時,將僅針對該特定分割槽執行查詢,由於將針對較少的資料量進行掃描,所以可以提供更好的效能。值得注意的是,分割槽欄位的選擇是影響查詢效能的重要因素,儘量避免層級較深的分割槽,這樣會造成太多的子資料夾。

現在問題來了,該使用哪些列進行分割槽呢?一條基本的法則是:選擇低基數屬性作為“分割槽鍵”,比如“地區”或“日期”等。

一些常見的分割槽欄位可以是:

  • 日期或者時間

比如year、month、day或者hour,當表中存在時間或者日期欄位時,可以使用些欄位。

  • 地理位置

比如國家、省份、城市等

  • 業務邏輯

比如部門、銷售區域、客戶等等

CREATETABLEtable_name(
col1data_type,
col2data_type)
PARTITIONEDBY(partition1data_type,partition2data_type,….);

3.分桶表

通常,當很難在列上建立分割槽時,我們會使用分桶,比如某個經常被篩選的欄位,如果將其作為分割槽欄位,會造成大量的分割槽。在Hive中,會對分桶欄位進行雜湊,從而提供了中額外的資料結構,進行提升查詢效率。

與分割槽表類似,分桶表的組織方式是將HDFS上的檔案分割成多個檔案。分桶可以加快資料取樣,也可以提升join的效能(join的欄位是分桶欄位),因為分桶可以確保某個key對應的資料在一個特定的桶內(檔案),所以巧妙地選擇分桶欄位可以大幅度提升join的效能。通常情況下,分桶欄位可以選擇經常用在過濾操作或者join操作的欄位。

我們可以使用set.hive.enforce.bucketing = true啟用分桶設定。

當使用分桶表時,最好將bucketmapjoin標誌設定為true,具體配置引數為:

SET hive.optimize.bucketmapjoin = true

CREATETABLEtable_name
PARTITIONEDBY(partition1data_type,partition2data_type,….)CLUSTEREDBY(column_name1,column_name2,…)
SORTEDBY(column_name[ASC|DESC],…)]
INTOnum_bucketsBUCKETS;

4.對中間資料啟用壓縮

複雜的Hive查詢通常會轉換為一系列多階段的MapReduce作業,並且這些作業將由Hive引擎連結起來以完成整個查詢。因此,此處的“中間輸出”是指上一個MapReduce作業的輸出,它將用作下一個MapReduce作業的輸入資料。

壓縮可以顯著減少中間資料量,從而在內部減少了Map和Reduce之間的資料傳輸量。

我們可以使用以下屬性在中間輸出上啟用壓縮。

sethive.exec.compress.intermediate=true;
sethive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec;
sethive.intermediate.compression.type=BLOCK;

為了將最終輸出到HDFS的資料進行壓縮,可以使用以下屬性:

sethive.exec.compress.output=true;

下面是一些可以使用的壓縮編解碼器

org.apache.hadoop.io.compress.DefaultCodec
org.apache.hadoop.io.compress.GzipCodec
org.apache.hadoop.io.compress.BZip2Codec
com.hadoop.compression.lzo.LzopCodec
org.apache.hadoop.io.compress.Lz4Codec
org.apache.hadoop.io.compress.SnappyCodec

5.Map端JOIN

map端join適用於當一張表很小(可以存在記憶體中)的情況,即可以將小表載入至記憶體。Hive從0.7開始支援自動轉為map端join,具體配置如下:

SEThive.auto.convert.join=true;--hivev0.11.0之後預設true
SEThive.mapjoin.smalltable.filesize=600000000;--預設25m
SEThive.auto.convert.join.noconditionaltask=true;--預設true,所以不需要指定mapjoinhint
SEThive.auto.convert.join.noconditionaltask.size=10000000;--控制載入到記憶體的表的大小
System.out.println(list.stream( www.haoranjupt.com).min((www.baihua178.cn b) -> a-b).get()); // 1
  
  System.out.println(www.wangffzc.cn list.stream(www.tengyueylzc.cn).count(www.baihuayllpt.cn));//
  
  String str =www.qitianylezc.cn"11,22,33,44,55";
  
  System.out.println(Stream.of(str.split(www.lthczcgw.cn",")).mapToInt(www.baihuayl7.cn -> Integer.valueOf(x)).sum());
  
  System.out.println(Stream.of(str.split("www.lanboylgw.com,")).mapToInt(Integer::valueOf).sum());
  
  System.out.println(Stream.of(str.split(www.shentuylzc.cn",")).map(x -www.javachenglei.com> Integer.valueOf(x)).mapToInt(x -> x).sum());
  
  System.out.println(Stream.of(str.split www.baihua178.cn,")).map(Integer::valueOf).mapToInt(x www.yuchengyule.com-> x).su

一旦開啟map端join配置,Hive會自動檢查小表是否大於hive.mapjoin.smalltable.filesize配置的大小,如果大於則轉為普通的join,如果小於則轉為map端join。

關於map端join的原理,如下圖所示:

首先,Task A(客戶端本地執行的task)負責讀取小表a,並將其轉成一個HashTable的資料結構,寫入到本地檔案,之後將其載入至分散式快取。

然後,Task B任務會啟動map任務讀取大表b,在Map階段,根據每條記錄與分散式快取中的a表對應的hashtable關聯,並輸出結果

注意:map端join沒有reduce任務,所以map直接輸出結果,即有多少個map任務就會產生多少個結果檔案。

6.向量化

Hive中的向量化查詢執行大大減少了典型查詢操作(如掃描,過濾器,聚合和連線)的CPU使用率。

標準查詢執行系統一次處理一行,在處理下一行之前,單行資料會被查詢中的所有運算子進行處理,導致CPU使用效率非常低。在向量化查詢執行中,資料行被批處理在一起(預設=> 1024行),表示為一組列向量。

要使用向量化查詢執行,必須以ORC格式(CDH 5)儲存資料,並設定以下變數。

SEThive.vectorized.execution.enabled=true

在CDH 6中預設啟用Hive查詢向量化,啟用查詢向量化後,還可以設定其他屬性來調整查詢向量化的方式,具體可以參考cloudera官網。

7.謂詞下推

預設生成的執行計劃會在可見的位置執行過濾器,但在某些情況下,某些過濾器表示式可以被推到更接近首次看到此特定資料的運算子的位置。

比如下面的查詢:

select
a.*,
b.*
from
ajoinbon(a.col1=b.col1)
wherea.col1>15andb.col2>16

如果沒有謂詞下推,則在完成JOIN處理之後將執行過濾條件**(a.col1> 15和b.col2> 16)**。因此,在這種情況下,JOIN將首先發生,並且可能產生更多的行,然後在進行過濾操作。

使用謂詞下推,這兩個謂詞**(a.col1> 15和b.col2> 16)**將在JOIN之前被處理,因此它可能會從a和b中過濾掉連線中較早處理的大部分資料行,因此,建議啟用謂詞下推。

通過將hive.optimize.ppd設定為true可以啟用謂詞下推。

SEThive.optimize.ppd=true

8.輸入格式選擇

Hive支援TEXTFILE, SEQUENCEFILE, AVRO, RCFILE, ORC,以及PARQUET檔案格式,可以通過兩種方式指定表的檔案格式:

  • CREATE TABLE … STORE AS :即在建表時指定檔案格式,預設是TEXTFILE
  • ALTER TABLE … [PARTITION partition_spec] SET FILEFORMAT :修改具體表的檔案格式

如果未指定檔案儲存格式,則預設使用的是引數hive.default.fileformat設定的格式。

如果資料儲存在小於塊大小的小檔案中,則可以使用SEQUENCE檔案格式。如果要以減少儲存空間並提高效能的優化方式儲存資料,則可以使用ORC檔案格式,而當列中巢狀的資料過多時,Parquet格式會很有用。因此,需要根據擁有的資料確定輸入檔案格式。

9.啟動嚴格模式

如果要查詢分割槽的Hive表,但不提供分割槽謂詞(分割槽列條件),則在這種情況下,將針對該表的所有分割槽發出查詢,這可能會非常耗時且佔用資源。因此,我們將下面的屬性定義為strict,以指示在分割槽表上未提供分割槽謂詞的情況下編譯器將引發錯誤。

SEThive.partition.pruning=strict

10.基於成本的優化

Hive在提交最終執行之前會優化每個查詢的邏輯和物理執行計劃。基於成本的優化會根據查詢成本進行進一步的優化,從而可能產生不同的決策:比如如何決定JOIN的順序,執行哪種型別的JOIN以及並行度等。

可以通過設定以下引數來啟用基於成本的優化。

sethive.cbo.enable=true;
sethive.compute.query.using.stats=true;
sethive.stats.fetch.column.stats=true;
sethive.stats.fetch.partition.stats=true;

可以使用統計資訊來優化查詢以提高效能。基於成本的優化器(CBO)還使用統計資訊來比較查詢計劃並選擇最佳計劃。通過檢視統計資訊而不是執行查詢,效率會很高。

收集表的列統計資訊:

ANALYZETABLEmytableCOMPUTESTATISTICSFORCOLUMNS;

檢視my_db資料庫中my_table中my_id列的列統計資訊:

DESCRIBEFORMATTEDmy_db.my_tablemy_id