本文首發於公衆號:五分鐘學大數據node
hive 中的小文件確定是向 hive 表中導入數據時產生,因此先看下向 hive 中導入數據的幾種方式sql
insert into table A values (1,'zhangsan',88),(2,'lisi',61);
這種方式每次插入時都會產生一個文件,屢次插入少許數據就會出現多個小文件,可是這種方式生產環境不多使用,能夠說基本沒有使用的apache
load data local inpath '/export/score.csv' overwrite into table A -- 導入文件
load data local inpath '/export/score' overwrite into table A -- 導入文件夾
使用 load 方式能夠導入文件或文件夾,當導入一個文件時,hive表就有一個文件,當導入文件夾時,hive表的文件數量爲文件夾下全部文件的數量app
insert overwrite table A select s_id,c_name,s_score from B;
這種方式是生產環境中經常使用的,也是最容易產生小文件的方式ide
insert 導入數據時會啓動 MR 任務,MR中 reduce 有多少個就輸出多少個文件函數
因此, 文件數量=ReduceTask數量*分區數工具
也有不少簡單任務沒有reduce,只有map階段,則oop
文件數量=MapTask數量*分區數性能
每執行一次 insert 時hive中至少產生一個文件,由於 insert 導入時至少會有一個MapTask。
像有的業務須要每10分鐘就要把數據同步到 hive 中,這樣產生的文件就會不少。大數據
使用方法:
#對於非分區表
alter table A concatenate;
#對於分區表
alter table B partition(day=20201224) concatenate;
舉例:
#向 A 表中插入數據
hive (default)> insert into table A values (1,'aa',67),(2,'bb',87);
hive (default)> insert into table A values (3,'cc',67),(4,'dd',87);
hive (default)> insert into table A values (5,'ee',67),(6,'ff',87);
#執行以上三條語句,則A表下就會有三個小文件,在hive命令行執行以下語句
#查看A表下文件數量
hive (default)> dfs -ls /user/hive/warehouse/A;
Found 3 items
-rwxr-xr-x 3 root supergroup 378 2020-12-24 14:46 /user/hive/warehouse/A/000000_0
-rwxr-xr-x 3 root supergroup 378 2020-12-24 14:47 /user/hive/warehouse/A/000000_0_copy_1
-rwxr-xr-x 3 root supergroup 378 2020-12-24 14:48 /user/hive/warehouse/A/000000_0_copy_2
#能夠看到有三個小文件,而後使用 concatenate 進行合併
hive (default)> alter table A concatenate;
#再次查看A表下文件數量
hive (default)> dfs -ls /user/hive/warehouse/A;
Found 1 items
-rwxr-xr-x 3 root supergroup 778 2020-12-24 14:59 /user/hive/warehouse/A/000000_0
#已合併成一個文件
注意:
一、concatenate 命令只支持 RCFILE 和 ORC 文件類型。
二、使用concatenate命令合併小文件時不能指定合併後的文件數量,但能夠屢次執行該命令。
三、當屢次使用concatenate後文件數量不在變化,這個跟參數 mapreduce.input.fileinputformat.split.minsize=256mb 的設置有關,可設定每一個文件的最小size。
#執行Map前進行小文件合併
#CombineHiveInputFormat底層是 Hadoop的 CombineFileInputFormat 方法
#此方法是在mapper中將多個文件合成一個split做爲輸入
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; -- 默認
#每一個Map最大輸入大小(這個值決定了合併後文件的數量)
set mapred.max.split.size=256000000; -- 256M
#一個節點上split的至少的大小(這個值決定了多個DataNode上的文件是否須要合併)
set mapred.min.split.size.per.node=100000000; -- 100M
#一個交換機下split的至少的大小(這個值決定了多個交換機上的文件是否須要合併)
set mapred.min.split.size.per.rack=100000000; -- 100M
#設置map端輸出進行合併,默認爲true
set hive.merge.mapfiles = true;
#設置reduce端輸出進行合併,默認爲false
set hive.merge.mapredfiles = true;
#設置合併文件的大小
set hive.merge.size.per.task = 256*1000*1000; -- 256M
#當輸出文件的平均大小小於該值時,啓動一個獨立的MapReduce任務進行文件merge
set hive.merge.smallfiles.avgsize=16000000; -- 16M
# hive的查詢結果輸出是否進行壓縮
set hive.exec.compress.output=true;
# MapReduce Job的結果輸出是否使用壓縮
set mapreduce.output.fileoutputformat.compress=true;
#reduce 的個數決定了輸出的文件的個數,因此能夠調整reduce的個數控制hive表的文件數量,
#hive中的分區函數 distribute by 正好是控制MR中partition分區的,
#而後經過設置reduce的數量,結合分區函數讓數據均衡的進入每一個reduce便可。
#設置reduce的數量有兩種方式,第一種是直接設置reduce個數
set mapreduce.job.reduces=10;
#第二種是設置每一個reduce的大小,Hive會根據數據總大小猜想肯定一個reduce個數
set hive.exec.reducers.bytes.per.reducer=5120000000; -- 默認是1G,設置爲5G
#執行如下語句,將數據均衡的分配到reduce中
set mapreduce.job.reduces=10;
insert overwrite table A partition(dt)
select * from B
distribute by rand();
解釋:如設置reduce數量爲10,則使用 rand(), 隨機生成一個數 x % 10 ,
這樣數據就會隨機進入 reduce 中,防止出現有的文件過大或太小
Hadoop Archive簡稱HAR,是一個高效地將小文件放入HDFS塊中的文件存檔工具,它可以將多個小文件打包成一個HAR文件,這樣在減小namenode內存使用的同時,仍然容許對文件進行透明的訪問
#用來控制歸檔是否可用
set hive.archive.enabled=true;
#通知Hive在建立歸檔時是否能夠設置父目錄
set hive.archive.har.parentdir.settable=true;
#控制須要歸檔文件的大小
set har.partfile.size=1099511627776;
#使用如下命令進行歸檔
ALTER TABLE A ARCHIVE PARTITION(dt='2020-12-24', hr='12');
#對已歸檔的分區恢復爲原文件
ALTER TABLE A UNARCHIVE PARTITION(dt='2020-12-24', hr='12');
注意:
歸檔的分區能夠查看不能 insert overwrite,必須先 unarchive
若是是新集羣,沒有歷史遺留問題的話,建議hive使用 orc 文件格式,以及啓用 lzo 壓縮。
這樣小文件過多可使用hive自帶命令 concatenate 快速合併。
若是你想獲取更多大數據相關技術文章,可關注公衆號:五分鐘學大數據,專一於大數據技術研究,分享高質量的原創技術文章