產生背景:sqoop抽取oracle數據到hive表時,只能寫入到固定分區(--hive-partition-key #hive分區字段 --hive-partition-value #hive分區值)。於是先把數據抽取到一張增量表,然后從增量表動態寫入分區表。
set hive.exec.dynamic.partition.mode = true; --使用動態分區時,設置為ture。 set hive.exec.dynamic.partition.mode = nonstrict; --動態分區模式,默認值:strict,表示必須指定一個分區為靜態分區;nonstrict模式表示允許所有的分區字段都可以使用動態分區。一般需要設置為nonstrict。 set hive.exec.max.dynamic.partitions.pernode =10; --在每個執行MR的節點上,最多可以創建多少個動態分區,默認值:100。 set hive.exec.max.dynamic.partitions =1000; --在所有執行MR的節點上,最多一共可以創建多少個動態分區,默認值:1000。 set hive.exec.max.created.files = 100000; --整個MR Job中最多可以創建多少個HDFS文件,默認值:100000。 set hive.error.on.empty.partition = false; --當有空分區產生時,是否拋出異常,默認值:false。
Hive文件產生大量小文件的原因:
一是文件本身的原因:小文件多,以及文件的大小;
二是使用動態分區,可能會導致產生大量分區,從而產生很多小文件,也會導致產生很多Mapper;
三是Reduce數量較多,Hive SQL輸出文件的數量和Reduce的個數是一樣的。
小文件帶來的影響:
文件的數量和大小決定Mapper任務的數量,小文件越多,Mapper任務越多,每一個Mapper都會啟動一個JVM來運行,所以這些任務的初始化和執行會花費大量的資源,嚴重影響性能。
在NameNode中每個文件大約占150字節,小文件多,會嚴重影響NameNode性能。
解決小文件問題:
如果動態分區數量不可預測,最好不用。如果用,最好使用distributed by分區字段,這樣會對字段進行一個hash操作,把相同的分區給同一個Reduce處理;
減少Reduce數量;
進行以一些參數調整。
控制Mapper的數量:
決定Mapper的數量的因素有:輸入文件的個數,輸入文件的大小、集群設置的文件塊大小。
例如:輸入目錄下有1個800M的文件,hadoop會將文件分成7個文件(6*128M + 1*32M),從而產生7個Mapper數;
例如:輸入目錄下有5個文件,分別為:15M、20M、50M、100M、150M,那么hadoop會分隔成6個文件(15M、20M、50M、100M、128M、22M),從而產生6個Mapper。
可以通過設置如下參數,讓Map在執行之前合並小文件,從而減少Mapper數量:
set mapred.max.split.size=100000000; -- 決定每個map處理的最大的文件大小,單位為B set mapred.min.split.size.per.node=100000000; -- 節點中可以處理的最小的文件大小 set mapred.min.split.size.per.rack=100000000; -- 機架中可以處理的最小的文件大小 set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; ---實現map中的數據合並需要設置下面的參數,集群默認就是這個格式
控制Mapper的整體原則:
大數據量要利用合適的map數,單個map要處理合適的數據量;
map占用資源要合並小文件,map不足要把大文件拆成小文件。
控制Reduce的數量:
Reduce的個數會極大影響任務的執行效率
- Hive自己確定reduce數
不指定Reduce的個數的情況下,Hive會猜測確定一個Reduce個數,由下面兩個參數決定:
1、hive.exec.reducers.bytes.per.reducer(每個reduce任務處理的數據量,默認為1000^3=1G)
2、hive.exec.reducers.max(每個任務最大的reduce數,默認為999)
Reduce的個數N=min(參數2,輸入總數據量/參數1),例如:如果Reduce的輸入(map的輸出)總大小不超過1G,那么只有一個Reduce任務。
- 手動調整reduce數
Hive官網:
In order to change the average load for a reducer (in bytes): set hive.exec.reducers.bytes.per.reducer=<number> In order to limit the maximum number of reducers: set hive.exec.reducers.max=<number> In order to set a constant number of reducers: set mapreduce.job.reduces=<number>
Notes:動態分區采坑
在使用動態分區的時候,如果已知數據會分成n個分區,SQL運行的時候創建了m個Mapper,則這個SQL產生m * n個文件。如果這個數值大於設置的創建文件的總數(hive.exec.max.created.files),默認值100000個,就會出現異常。
在未知動態分區數時,可以使用distribute by 分區字段,將分區字段內容相同的數據放到同一個reduce,當然也可以使用distribute by rand()將數據隨記分配給reduce,這樣可以使每個reduce處理的數據大體相同。
- 和map一樣,啟動和初始化reduce會消耗時間和資源,有多少reduce就會產生多少個文件
- 以下情況下,會只有一個reduce:
- 沒有group by的匯總,如把select dt,count(1) from test where dt = '2019-12-12' group by dt;,寫成select count(1) from test where dt = '2019-12-12';
- 用了order by;
- 有笛卡爾積;
控制Reduce的整體原則:
使大數據量利用合適的reduce數;
使單個reduce任務處理合適的數據量。
