真正讓你明白Hive參數調優系列1:控制map個數與性能調優參數

     本系列幾章系統地介紹了開發中Hive常見的用戶配置屬性(有時稱爲參數變量選項),並說明了哪些版本引入了哪些屬性,常見有哪些屬性的使用,哪些屬性可以進行Hive調優,以及如何使用的問題。以及日常Hive開發中如何進行性能調優。

1.Hive有哪些參數,如何查看這些參數

  1. Hive自帶的配置屬性列表封裝在HiveConfJava類中,因此請參閱該HiveConf.java文件以獲取Hive版本中可用的配置屬性的完整列表。具體可以下載hive.src通過eclipse查看。全部屬性有上千個吧,一般Hive的自帶屬性都是以hive.開頭的,每個屬性且自帶詳細的描述信息,其次Hive官網也有,但是屬性不是特別全。Hive官方參數網址
  2. Hive除了自身帶了一些配置屬性,因爲其底層使用的是hadoop(HDFS,MR,YARN),所以有些HADOOP的配置屬性Hive也可以使用,進行配置,但是有些則使用不了。比如mapred.max.split.size 就屬於MR的參數,但是hive可以使用。

2.map個數的控制參數與性能調優

     很顯然,對於這個控制每個map的split輸入大小的參數,不是hive自帶的參數,而是MR提供的參數,但是Hive可以通過set的形式配置使用,而且對於調優有很大的作用。但是這個參數實際上要配合HDFS的blocksize一起使用下面以我們公司開發環境的默認配置參數。

-- 每個Map最大輸入大小,
hive> set mapred.max.split.size;
mapred.max.split.size=256000000  這也是官方默認值
-- 每個Map最小輸入大小
hive> set mapred.min.split.size;
mapred.min.split.size=10000000   這也是官方默認值
hive> set dfs.block.size;
dfs.block.size=134217728   我們集羣默認hdfs的block塊大小是128Mb,但注意這個參數通過hive設置更改實際沒有用的,只能hdfs設置。

2.1數據準備,兩張表

    如下進行兩張表join,其中每張表的大小,hdfs上存儲的文件總個數,以及每個文件的大小。

  1. 大表總共158749566行,文件總大小4.4G,存儲個數22個文件,每個大小200Mb左右。
  2. 小表總共1979375 行,文件大小50.7Mb,存儲個數2個文件,大小50Mb以內。
[finance@hadoop-client13-prd ~]$ hadoop fs -du  -h  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d
206.7 M  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d/000000_0.deflate
.....省略.................
hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d/000021_0.deflate
---------------------------------------------------------------------------------------
[finance@hadoop-client13-prd ~]$ hadoop fs -du  -h  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/t_fgeics_company_liquidation_d_tmp
36.4 M  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/t_fgeics_company_liquidation_d_tmp/000000_0.deflate
14.3 M  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/t_fgeics_company_liquidation_d_tmp/000001_0.deflate

2.2兩個表進行關聯,其中小表在前,大表在後

2.2.1如下,運行如下代碼,實現兩個表進行關聯。

set mapred.max.split.size=134217728;
set mapred.min.split.size=134217728;
drop table IF EXISTS fdm_tmp.company_liquidation_fgeics_company_ar_d_tmp;
create table fdm_tmp.company_liquidation_fgeics_company_ar_d_tmp
as 
select
 a.id
,a.entid
,a.ancheyear
,b.liqmen
,b.ligprincipal
,a.regno
,a.tel
,a.postalcode
,a.dom
,a.email
,a.busst
,a.empnum
,a.name
,a.updated
,b.etl_time
from fdm_tmp.t_fgeics_company_liquidation_d_tmp  b
right join fdm_tmp.company_liquidation_fgeics_company_ar_d  a
on b.entid = a.entid;

Hadoop job information for Stage-1: number of mappers: 24; number of reducers: 17

結果分析:hive啓動了24個map函數,17個reduce函數。在hadoop中,一般默認的split切片小於等於blocksize(128Mb),如果是小文件的話(未進行小文件的合併)則每個小文件啓動一個map函數。而實際在hive中,並不是split的大小要小於等於blocksize,而是可以遠大於blocksize。比如這裏,4.4G文件表,按128Mb切片算的話,至少實際需要35個map,而實際只需要24個,平均每個map處理了187Mb的文件。這是爲什麼呢?此外這裏明明設置了set mapred.max.split.size=134217728,每個map最大split塊是 128Mb,而實際爲什麼參數沒有用呢?網上有很多關於這方面的文章,但是幾乎都是轉載抄襲,沒有任何深入理解,或者深入剖析決定map個數的原因。

3.案例演示決定map個數的因素

       其實決定map個數的因素有很多,比如文件是否壓縮,壓縮的後的文件是否支持切分,比如文件默認的inputfort格式,不同實現類的split算法也不同,那麼map的個數調優方式也不同,下面按分類詳細說明hive中決定map個數的因素與常見map調優的使用。

首先分類:處理的文件是否壓縮,且壓縮算法是否支持文件的切分

 如下我們公司,對於hive關於壓縮的配置,使用了壓縮,且使用的是默認的壓縮算法是deflate方法。

hive>  set io.compression.codecs; --配置了哪些壓縮算法
io.compression.codecs=org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec,org.apache.hadoop.io.compress.BZip2Codec
hive> set hive.exec.compress.output;
hive.exec.compress.output=true  --是否開啓壓縮
hive> set  mapreduce.output.fileoutputformat.compress.codec;  --使用的壓縮算法
mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.DefaultCodec

我們知道hive中有些壓縮算法是不支持文件切分的,如下我們使用的默認的deflate算法,就不支持文件切分。

 3.1.文件使用了壓縮,且壓縮算法不支持文件切分

------------------------使用不同參數執行上面代碼產生的map個數---------------------------------
--1.使用系統配置的默認值
set mapred.max.split.size  = 256000000; --
set mapred.min.split.size = 256000000;
Hadoop job information for Stage-1: number of mappers: 24; number of reducers: 17

--2.降低系統默認值
set mapred.max.split.size=134217728;
set mapred.min.split.size=134217728;
Hadoop job information for Stage-1: number of mappers: 24; number of reducers: 17

--3.調高系統默認值
set mapred.max.split.size=500000000;
set mapred.min.split.size=256000000;
Hadoop job information for Stage-1: number of mappers: 9; number of reducers: 17

--4.調高系統默認值
set mapred.max.split.size=1024000000;
set mapred.min.split.size=1024000000;
Hadoop job information for Stage-1: number of mappers:6 ; number of reducers: 17

       如上我們使用不同的參數配置,來運行上面同一段代碼,看系統產生的map個數,細心的人會發現,當我們使用默認值是產生了24個map,平均每個map處理了187Mb文件,但當我們調低set mapred.max.split.size=134217728時(每個map最多處理128Mb),相應的map個數並沒有增加,這是爲什麼呢?

       關於這個問題就要說到決定map個數的首要因素:文件是否啓動壓縮,且壓縮算法是否支持文件切分了。因爲這裏文件存儲使用默認的deflate算法,不支持文件切分,所以設置的參數split.size=134217728沒有生效。因爲每個map處理的splitsize實際上要大於等於每個文件存儲的大小。這裏每個文件存儲的大小200Mb左右,所以每個map處理的最小尺寸要大於200Mb。

      而當我們將set mapred.max.split.size=102400000設置的很大時,爲什麼又可以控制map個數了呢?因爲deflate壓縮算法雖然不支持文件切分,但是可以進行文件合併哇。從hive0.5開始就默認map前進行小文件合併了。如下,我們公司使用的也是默認的開啓map前文件合併。但是注意即使這裏支持文件合併,也是基於文件塊的整個文件塊合併,而不是基於blocksize的block合併。

hive> set hive.input.format; --hive0.5開始的默認值,這個值會影響map個數的控制
hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat

尖叫提示1: 

         通過上面分析總結,當hive需要處理的文件是壓縮,且壓縮算法不支持文件切分的時候,決定map個數的因素主要是文件塊實際存儲的大小,如果文件塊本身很大,比如500Mb左右,那麼每個map處理的splitsize至少要是500Mb左右。這個時候我們不能人爲通過參數降低每個map的splitsize來增加map個數,只能通過增加splitsize,減少map個數。

        但是一般經驗來說,每個map處理的splitsize最好是128Mb(等於blocksize),這樣效率最高。所以這個時候如果我們想增加map個數,只能通過臨時表或者insert ...select的形式,通過參數配置將文件塊重新存儲成較小的文件塊,然後再進行處理。反之,如果文件塊本身很小,那麼我們可以通過增加splitsize來減少map,進而調優提高程序的運行效率。

尖叫總結1:

        如果hive處理的文件是壓縮模式,且壓縮模式不支持文件切分,那麼這個時候我們只能通過控制參數來減少map個數,而不能通過配置參數來增加map個數,所以Hive對於壓縮不可切分文件的調優有限。可以首先通過hadoop fs -du -s -h命令查看文件的存儲大小結果,然後根據實際進行調優。常用的配置參數如下:

set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; --hive0.5開始就是默認值,執行map前進行小文件合併
----------------------------------------------------------------------
set mapred.max.split.size=256000000  
set mapred.min.split.size=10000000
set mapred.min.split.size.per.node=8000000 --每個節點處理的最小split
set mapred.min.split.size.per.rack=8000000 --每個機架處理的最小slit.
------------------------------------------------------------------------
1.注意一般來說這四個參數的配置結果大小要滿足如下關係。
max.split.size >= min.split.size >= min.size.per.node >= min.size.per.node

2.這四個參數的作用優先級分別如下
max.split.size <= min.split.size <= min.size.per.node <= min.size.per.node

比如如下,同樣上面的代碼,我們將其參數設置如下,發現只啓動了12個map,故max.split.size沒有起作用。
當四個參數設置矛盾時,系統會自動以優先級最高的參數爲準,進行計算
set mapred.max.split.size=300000000;
set mapred.min.split.size.per.node=300000000;
set mapred.min.split.size.per.rack=300000000;
Hadoop job information for Stage-1: number of mappers: 12; number of reducers: 17

3.注意這四個參數可以選擇性設置,可以選擇性設置大小或者使用默認值,但仍遵循前兩條規則。

3.2 文件未使用壓縮,或壓縮算法支持文件切分

        同樣是上面那個4.4g文件,我們這個時候讓其爲非壓縮模式,發現這個時候文件總大小爲23.4G,存儲爲22個文件,平均每個文件大小都在1.1G左右。所以壓縮有時候是個好東西。如下我們所有非壓縮的性能測試基於此文件。

[finance]$ hadoop fs -count hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d
1           22        25154158871 hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d
-----------------------------------------------------------------------------------------------------------------------------------
[finance]$ hadoop fs -du -s -h hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d
23.4 G  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d
-----------------------------------------------------------------------------------------------------------------------------------
[finance]$ hadoop fs -du -h hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d
1.1 G  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d/000000_0
1.1 G  hdfs://suninghadoop2/user/finance/hive/warehouse/fdm_tmp.db/company_liquidation_fgeics_company_ar_d/000001_0
...............................

3.2.1.若這時set hive.input.format爲HiveInputFormat

hive> set hive.input.format; --從hive0.5就默認是CombineHiveInputFormat,所以這個用的不多
hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat;

   如果這時set hive.input.format爲HiveInputFormat,這個時候有以下三個屬性值來確定InputSplit的個數:

set mapred.map.tasks=2    --我們公司使用的是默認2
set mapred.max.split.size=256000000  --我們公司默認值
set mapred.min.split.size=10000000   ---我們公司默認值
set  dfs.block.size=134217728   --128Mb
----------------------------------------------------------------------------------------
1. goalSize:該值由 totalSize/numSplits  totlasize文件塊大小,numslplits=mapred.map.tasks=2 
2. minSize:由配置參數 mapred.min.split.size(或者新版本的mapreduce.input.fileinputformat.split.minsize)mapred.min.split.size=10000000 
   決定的 InputFormat的最小長度,默認爲1,我們公司默認值是
3.blockSize:HDFS 中的文件存儲塊block的大小,默認爲128MB。

這三個參數決定一個 InputFormat 分片的最終的長度,計算方法如下:
splitSize = max{minSize,min{goalSize,blockSize}} 

尖叫總結2:

     1.當hive處理的文件是非壓縮或者壓縮可切分,且hive.input.format爲HiveInputFormat時,這個時候決定map個數的參數主要是splitSize = max{minSize,min{goalSize,blockSize}} ,只有這個時候一般map的splitsize小於等於blocksize(128Mb)。但其實這種方式現在企業實際開發中已經使用的很少了。

3.2.2.若hive.input.format爲默認CombineHiveInputFormat

    1.如下,使用公司默認的map函數split參數,發現未壓縮的23.4g的文件(22個)這裏共使用了112個map函數,符合參數的設置結果set mapred.max.split.size=256000000  ;  23.4*1024/112=187Mb<256000000。

--使用默認配置參數,實現對非壓縮文件的操作
set mapred.max.split.size=256000000  ;
set mapred.min.split.size=10000000;

drop table IF EXISTS fdm_tmp.company_liquidation_fgeics_company_ar_d_tmp;
create table fdm_tmp.company_liquidation_fgeics_company_ar_d_tmp
as 
select
 a.id
,a.entid
,a.ancheyear
,b.liqmen
,b.ligprincipal
,a.regno
,a.tel
,a.postalcode
,a.dom
,a.email
,a.busst
,a.empnum
,a.name
,a.updated
,b.etl_time
from fdm_tmp.t_fgeics_company_liquidation_d_tmp  b
right join fdm_tmp.company_liquidation_fgeics_company_ar_d  a
on b.entid = a.entid;

Hadoop job information for Stage-1: number of mappers: 112; number of reducers: 85
Time taken: 152.679 seconds

   2.如下,將上面默認的參數對應調小,運行同樣上面的代碼,看map數是否有增加? 很顯然,我們通過設置max.split.size的值實現了增加map個數的功能。這裏map的個數由122個數變成了199個,平均每個map處理數據120Mb左右。但是運行時間卻變慢了很多,時間由153s變成了243s。所以map的split大小並不是要接近blocksize才高效,這主要跟集羣的性能配置有關。

--將map的split參數最大值設置爲128Mb.
set mapred.max.split.size=134217728 ;
set mapred.min.split.size=10000000;

Hadoop job information for Stage-1: number of mappers: 199; number of reducers: 85
Time taken: 243.038 seconds

  3.如下,將上面的默認參數增加,運行同樣的代碼,結果雖然我們將maxsplitsize設置的特別大,但是對應map的個數並沒有對應的成倍減少,如果按最大值算應該在20多個map,而實際不是這樣。這說明,光設置最大值是沒有用的,這只是一個峯值,還有對應的設置最小值。

--只將max.split.size設置的特別大,min.split.size還是10Mb左右。
set mapred.max.split.size=1024000000;
set mapred.min.split.size= 10000000;
Hadoop job information for Stage-1: number of mappers: 106; number of reducers: 85

4.map的多個參數配合使用,精確控制map的個數 


--1.只將max.split.size設置的特別大,且將 min.split.size使用默認值,發現map個數沒有成倍減少。
set mapred.max.split.size=1024000000;
set mapred.min.split.size=10000000;
Hadoop job information for Stage-1: number of mappers: 106; number of reducers: 85
------------------------------------------------------------------------------------
--2.同時將max.split.size設置的特別大,且將 min.split.size同時設置很大爲256Mb左右
    但是發現map的個數並沒有減少,還是和上面一樣,這說明控制map的個數還有別的因素
set mapred.max.split.size=1024000000;
set mapred.min.split.size=256000000;
Hadoop job information for Stage-1: number of mappers: 106; number of reducers: 85
------------------------------------------------------------------------------------
---3.配合min.split.size.per.node使用,發現map個數仍然沒有減少

set mapred.max.split.size=1024000000;
set mapred.min.split.size= 256000000;
set mapred.min.split.size.per.node=256000000;--默認值是800000
set mapred.min.split.size.per.rack=800000;---默認值
--------------------------------------------------------------------------------------
---4.配合min.split.size.per.rack使用,map個數精準減少了,每個map處理的數據在256和1024之間
Hadoop job information for Stage-1: number of mappers: 106; number of reducers: 85
set mapred.max.split.size=1024000000;
set mapred.min.split.size= 256000000;
set mapred.min.split.size.per.node=256000000;
set mapred.min.split.size.per.rack=256000000;

Hadoop job information for Stage-1: number of mappers: 88; number of reducers: 85

尖叫總結3:

       如果Hive處理的的文件爲非壓縮格式或者壓縮可切分,且inputFormat爲CombineHiveInputFormat時,則控制map個數是由以下四個參數起作用,關於這四個參數作用優先級與使用注意事項請參考如下。

mapred.min.split.size 或者 mapreduce.input.fileinputformat.split.minsize。
mapred.max.split.size 或者 mapreduce.input.fileinputformat.split.maxsize。
mapred.min.split.size.per.rack 或者 mapreduce.input.fileinputformat.split.minsize.per.rack。
mapred.min.split.size.per.node 或者 mapreduce.input.fileinputformat.split.minsize.per.node。

set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; --hive0.5開始就是默認值,執行map前進行小文件合併
----------------------------------------------------------------------
set mapred.max.split.size=256000000   --公司集羣默認值
set mapred.min.split.size=10000000     --公司集羣默認值
set mapred.min.split.size.per.node=8000000 --每個節點處理的最小split
set mapred.min.split.size.per.rack=8000000 --每個機架處理的最小slit.
------------------------------------------------------------------------
1.注意一般來說這四個參數的配置結果大小要滿足如下關係。
max.split.size >= min.split.size >= min.size.per.node >= min.size.per.node

2.這四個參數的作用優先級分別如下
max.split.size <= min.split.size <= min.size.per.node <= min.size.per.node

比如如下,同樣上面的代碼,我們將其參數設置如下,發現只啓動了12個map,故max.split.size沒有起作用。
當四個參數設置矛盾時,系統會自動以優先級最高的參數爲準,進行計算
set mapred.max.split.size=300000000;
set mapred.min.split.size.per.node=300000000;
set mapred.min.split.size.per.rack=300000000;
Hadoop job information for Stage-1: number of mappers: 12; number of reducers: 17

3.注意這四個參數可以選擇性設置,可以選擇性設置大小或者使用默認值,但仍遵循前兩條規則。

       所以如果對於Hive調優,想通過控制map個數進行調優,首先確定集羣是否啓動了壓縮,且壓縮的算法是否直接文件切分,然後再確定集羣配置的默認的hive.input.format是什麼實現類,不同實現類對於split的算法不同,當然控制map的參數也不同。所以對於控制map個數調優遠遠不是網上很多人說的那麼簡單。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章