1. 程式人生 > 實用技巧 >hive調優

hive調優

10.hive調優

10.1 Fetch抓取

Hive中對某些情況的查詢可以不必使用MapReduce計算。例如:SELECT * FROM score;在這種情況下,Hive可以簡單地讀取score對應的儲存目錄下的檔案,然後輸出查詢結果到控制檯。通過設定hive.fetch.task.conversion引數,可以控制查詢語句是否走MapReduce.

案例實操:

1)把hive.fetch.task.conversion設定成none,然後執行查詢語句,都會執行mapreduce程式。

set hive.fetch.task.conversion=none;

select * from score;
select s_score from score;
select s_score from score limit 3;

2)把hive.fetch.task.conversion設定成more,然後執行查詢語句,如下查詢方式都不會執行mapreduce程式。

set hive.fetch.task.conversion=more;

select * from score;
select s_score from score;
select s_score from score limit 3;

###10.2 本地模式

大多數的Hadoop Job是需要Hadoop提供的完整的可擴充套件性來處理大資料集的。不過,有時Hive的輸入資料量是非常小的。在這種情況下,為查詢觸發執行任務時消耗可能會比實際job的執行時間要多的多。對於大多數這種情況,Hive可以通過本地模式在單臺機器上處理所有的任務。對於小資料集,執行時間可以明顯被縮短。

使用者可以通過設定hive.exec.mode.local.auto的值為true,來讓Hive在適當的時候自動啟動這個優化。

案例實操:

1)開啟本地模式,並執行查詢語句

set hive.exec.mode.local.auto=true; 
select * from score cluster by s_id;

2)關閉本地模式,並執行查詢語句

set hive.exec.mode.local.auto=false; 
select * from score cluster by s_id;

###10.3 MapJoin

如果不指定MapJoin或者不符合MapJoin的條件,那麼Hive解析器會在Reduce階段完成join,容易發生資料傾斜。可以用MapJoin把小表全部載入到記憶體在map端進行join,避免reducer處理。

1)開啟MapJoin引數設定:

(1)設定自動選擇Mapjoin

set hive.auto.convert.join = true; 

(2)大表小表的閾值設定(預設25M以下認為是小表):

set hive.mapjoin.smalltable.filesize=25123456;

###10.4 Group By

預設情況下,Map階段同一Key資料分發給一個reduce,當一個key資料過大時就傾斜了。並不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端進行部分聚合,最後在Reduce端得出最終結果。

開啟Map端聚合引數設定

(1)是否在Map端進行聚合,預設為True

set hive.map.aggr = true;

(2)在Map端進行聚合操作的條目數目

 set hive.groupby.mapaggr.checkinterval = 100000;

(3)有資料傾斜的時候進行負載均衡(預設是false)

 set hive.groupby.skewindata = true;

當選項設定為 true,生成的查詢計劃會有兩個MR Job。

第一個MR Job中,Map的輸出結果會隨機分佈到Reduce中,每個Reduce做部分聚合操作,並輸出結果,這樣處理的結果是相同的Group By Key有可能被分發到不同的Reduce中,從而達到負載均衡的目的;

第二個MR Job再根據預處理的資料結果按照Group By Key分佈到Reduce中(這個過程可以保證相同的Group By Key被分佈到同一個Reduce中),最後完成最終的聚合操作。

###10.5 Count(distinct)

資料量小的時候無所謂,資料量大的情況下,由於COUNT DISTINCT操作需要用一個Reduce Task來完成,這一個Reduce需要處理的資料量太大,就會導致整個Job很難完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替換:

select count(distinct s_id) from score;
select count(s_id) from (select id from score group by s_id) a;

雖然會多用一個Job來完成,但在資料量大的情況下,這個絕對是值得的。

###10.6 笛卡爾積

儘量避免笛卡爾積,即避免join的時候不加on條件,或者無效的on條件,Hive只能使用1個reducer來完成笛卡爾積。

###10.7 動態分割槽調整

往hive分割槽表中插入資料時,hive提供了一個動態分割槽功能,其可以基於查詢引數的位置去推斷分割槽的名稱,從而建立分割槽。使用Hive的動態分割槽,需要進行相應的配置。

Hive的動態分割槽是以第一個表的分割槽規則,來對應第二個表的分割槽規則,將第一個表的所有分割槽,全部拷貝到第二個表中來,第二個表在載入資料的時候,不需要指定分割槽了,直接用第一個表的分割槽即可

####10.7.1 開啟動態分割槽引數設定

(1)開啟動態分割槽功能(預設true,開啟)

set hive.exec.dynamic.partition=true;

(2)設定為非嚴格模式(動態分割槽的模式,預設strict,表示必須指定至少一個分割槽為靜態分割槽,nonstrict模式表示允許所有的分割槽欄位都可以使用動態分割槽。)

set hive.exec.dynamic.partition.mode=nonstrict;

(3)在所有執行MR的節點上,最大一共可以建立多少個動態分割槽。

set  hive.exec.max.dynamic.partitions=1000;

(4)在每個執行MR的節點上,最大可以建立多少個動態分割槽。該引數需要根據實際的資料來設定。

set hive.exec.max.dynamic.partitions.pernode=100

(5)整個MR Job中,最大可以建立多少個HDFS檔案。

在linux系統當中,每個linux使用者最多可以開啟1024個程序,每一個程序最多可以開啟2048個檔案,即持有2048個檔案控制代碼,下面這個值越大,就可以開啟檔案控制代碼越大

set hive.exec.max.created.files=100000;

(6)當有空分割槽生成時,是否丟擲異常。一般不需要設定。

set hive.error.on.empty.partition=false;

####10.7.2 案例操作

需求:將ori中的資料按照時間(如:20111231234568),插入到目標表ori_partitioned的相應分割槽中。

(1)準備資料原表

create table ori_partitioned(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) 
PARTITIONED BY (p_time bigint)
row format delimited fields terminated by '\t';

load data local inpath '/export/servers/hivedatas/small_data' into table ori_partitioned partition (p_time='20111230000010');

load data local inpath '/export/servers/hivedatas/small_data' into table ori_partitioned partition (p_time='20111230000011');

(2)建立目標分割槽表

create table ori_partitioned_target(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) PARTITIONED BY (p_time STRING) row format delimited fields terminated by '\t'

(3)向目標分割槽表載入資料

如果按照之前介紹的往指定一個分割槽中Insert資料,那麼這個需求很不容易實現。這時候就需要使用動態分割槽來實現。

INSERT overwrite TABLE ori_partitioned_target PARTITION (p_time)
SELECT id, time, uid, keyword, url_rank, click_num, click_url, p_time
FROM ori_partitioned;

注意:在SELECT子句的最後幾個欄位,必須對應前面PARTITION (p_time)中指定的分割槽欄位,包括順序。

(4)檢視分割槽

show partitions ori_partitioned_target; 

10.8 並行執行

Hive會將一個查詢轉化成一個或者多個階段。這樣的階段可以是MapReduce階段、抽樣階段、合併階段、limit階段。或者Hive執行過程中可能需要的其他階段。預設情況下,Hive一次只會執行一個階段。不過,某個特定的job可能包含眾多的階段,而這些階段可能並非完全互相依賴的,也就是說有些階段是可以並行執行的,這樣可能使得整個job的執行時間縮短。不過,如果有更多的階段可以並行執行,那麼job可能就越快完成。

通過設定引數hive.exec.parallel值為true,就可以開啟併發執行。不過,在共享叢集中,需要注意下,如果job中並行階段增多,那麼叢集利用率就會增加。

set hive.exec.parallel = true;

當然,得是在系統資源比較空閒的時候才有優勢,否則,沒資源,並行也起不來。

10.9 嚴格模式

Hive提供了一個嚴格模式,可以防止使用者執行那些可能意向不到的不好的影響的查詢。

通過設定屬性hive.mapred.mode值為預設是非嚴格模式nonstrict 。開啟嚴格模式需要修改hive.mapred.mode值為strict,開啟嚴格模式可以禁止3種類型的查詢。

set hive.mapred.mode = strict; #開啟嚴格模式
set hive.mapred.mode = nostrict; #開啟非嚴格模式

1)對於分割槽表,在where語句中必須含有分割槽欄位作為過濾條件來限制範圍,否則不允許執行。換句話說,就是使用者不允許掃描所有分割槽。進行這個限制的原因是,通常分割槽表都擁有非常大的資料集,而且資料增加迅速。沒有進行分割槽限制的查詢可能會消耗令人不可接受的巨大資源來處理這個表。

2)對於使用了order by語句的查詢,要求必須使用limit語句。因為order by為了執行排序過程會將所有的結果資料分發到同一個Reducer中進行處理,強制要求使用者增加這個LIMIT語句可以防止Reducer額外執行很長一段時間。

3)限制笛卡爾積的查詢。對關係型資料庫非常瞭解的使用者可能期望在執行JOIN查詢的時候不使用ON語句而是使用where語句,這樣關係資料庫的執行優化器就可以高效地將WHERE語句轉化成那個ON語句。不幸的是,Hive並不會執行這種優化,因此,如果表足夠大,那麼這個查詢就會出現不可控的情況。

10.10 JVM重用

JVM重用是Hadoop調優引數的內容,其對Hive的效能具有非常大的影響,特別是對於很難避免小檔案的場景或task特別多的場景,這類場景大多數執行時間都很短。

Hadoop的預設配置通常是使用派生JVM來執行map和Reduce任務的。這時JVM的啟動過程可能會造成相當大的開銷,尤其是執行的job包含有成百上千task任務的情況。JVM重用可以使得JVM例項在同一個job中重新使用N次。N的值可以在Hadoop的mapred-site.xml檔案中進行配置。通常在10-20之間,具體多少需要根據具體業務場景測試得出。

我們也可以在hive當中通過

set  mapred.job.reuse.jvm.num.tasks=10;

這個設定來設定我們的jvm重用

這個功能的缺點是,開啟JVM重用將一直佔用使用到的task插槽,以便進行重用,直到任務完成後才能釋放。如果某個“不平衡的”job中有某幾個reduce task執行的時間要比其他Reduce task消耗的時間多的多的話,那麼保留的插槽就會一直空閒著卻無法被其他的job使用,直到所有的task都結束了才會釋放。

10.11 推測執行

在分散式叢集環境下,因為程式Bug(包括Hadoop本身的bug),負載不均衡或者資源分佈不均等原因,會造成同一個作業的多個任務之間執行速度不一致,有些任務的執行速度可能明顯慢於其他任務(比如一個作業的某個任務進度只有50%,而其他所有任務已經執行完畢),則這些任務會拖慢作業的整體執行進度。為了避免這種情況發生,Hadoop採用了推測執行(Speculative Execution)機制,它根據一定的法則推測出“拖後腿”的任務,併為這樣的任務啟動一個備份任務,讓該任務與原始任務同時處理同一份資料,並最終選用最先成功執行完成任務的計算結果作為最終結果。

設定開啟推測執行引數:

set mapred.map.tasks.speculative.execution=true
set mapred.reduce.tasks.speculative.execution=true
set hive.mapred.reduce.tasks.speculative.execution=true;

關於調優這些推測執行變數,還很難給一個具體的建議。如果使用者對於執行時的偏差非常敏感的話,那麼可以將這些功能關閉掉。如果使用者因為輸入資料量很大而需要執行長時間的map或者Reduce task的話,那麼啟動推測執行造成的浪費是非常巨大大。

###