hive小檔案過多問題解決
技術標籤:問題總結hadoopSQLhive大資料hadoop
起因
資料中臺當前有一張流水類表,存在3200個分割槽,230w個數據檔案,150億條資料,導致該表查詢起來及其麻煩,更令人糟心的是,業務人員不懂查詢方式,經常有人使用select *
的方式查詢該表,導致hiveserver2
經常炸掉,極大影響叢集的使用,因此,我們決定處理掉這個問題。
我們來看下是什麼原因導致這個問題
首先,檔案數量和大小會影響Mapper任務的數量,所以小檔案越多,mapper任務越多,每個mapper任務會啟動一個JVM,所以這些任務初始化和執行會消耗大量資源。而且在NameNode中每個檔案大約佔150位元組,小檔案問題會直接帶來NameNode的壓力巨大,從而導致HDFS的穩定性,同時對HDFS日常的資料讀寫帶來效能下降。
解決方法
-
從源頭解決,在日增資料的指令碼中加入引數設定,約束生成的小檔案數量,引數示例如下
set mapred.max.split.size=25000000; set mapred.min.split.size.per.node=10000000; set hive.hadoop.supports.splittable.combineinputformat=true; set mapred.min.split.size.per.rack=10000000; set hive.exec.compress.output=true; set hive.merge.mapfiles=true; set hive.merge.mapredfiles=true; set hive.merge.size.per.task=256000000; set hive.merge.smallfiles.avgsize=16000000; set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
附贈一份從其他地方copy過來的引數說明
引數 | 說明 |
---|---|
----- | 設定Map輸入合併小檔案的相關引數 |
set mapred.max.split.size=256000000 | 每個Map最大輸入大小(這個值決定了合併後文件的數量) |
set mapred.min.split.size.per.node=100000000 | 一個節點上split的至少的大小(這個值決定了多個DataNode上的檔案是否需要合併) |
set mapred.min.split.size.per.rack=100000000 | 一個交換機下split至少的大小(這個值決定了多個交換機上的檔案是否需要合併) |
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; | 執行Map前進行小檔案合併 |
----- | 設定Map輸出和Reduce輸出進行合併的相關引數 |
set hive.merge.mapfiles = true | 設定map端輸出進行合併,預設為true |
set hive.merge.mapredfiles = true | 設定reduce端輸出進行合併,預設為false |
set hive.merge.size.per.task = 25610001000 | 設定合併檔案的大小 |
set hive.merge.smallfiles.avgsize=16000000 | 當輸出檔案的平均大小小於該值時,啟動一個獨立的MapReduce任務進行檔案的Merge |
----- | 設定 |
set parquet.compression=GZIP | parquet格式的壓縮格式 |
set hive.exec.reducers.bytes.per.reducer=115000000 | 設定執行時Reduce的數量 |
set hive.exec.reducers.max=120000000; | 設定執行時Reduce的最大數量 |
-
合併hdfs中已有的小檔案,這也是本文的重點
合併小檔案有兩種思路。一是使用hive的原生工具
concatenate
來合併小檔案,sql示例如下
alter table app.example_orc partition (dt=“20200202”) concatenate;
但是,concatenate只支援RCFile和ORC格式的表,在其他如parquet格式中不適用,因此,如果是新的數倉,可以直接規定所有表使用orc格式來支援這個工具,這樣小檔案過多可以使用hive自帶命令 concatenate 快速合併。
Tips:
1、concatenate 命令只支援 RCFILE 和 ORC 檔案型別。
2、使用concatenate命令合併小檔案時不能指定合併後的檔案數量,但可以多次執行該命令。
3、當多次使用concatenate後文件數量不在變化,這個跟引數 mapreduce.input.fileinputformat.split.minsize=256mb 的設定有關,可設定每個檔案的最小size。
4、只能針對分割槽使用concatenate命令。
另一種方法就顯得比較繁瑣和笨重,那就是通過新建臨時表,將現有分割槽中的表匯入臨時表再回寫的方法來解決該問題,因為我們系統中該表只需要處理部分分割槽,因此考慮這樣處理,如果你是全部分割槽均存在這種問題,可以考慮直接通過把備用表改名的形式,避免多一次I/O。
示例程式碼如下
- 建立臨時表
create table bs_collect.tsconfirm_transtion like bs_collect.tsconfirm;
--查詢小檔案數量命令
--hdfs dfs -count {path}
- 資料寫入臨時表
set mapred.max.split.size=25000000;
set mapred.min.split.size.per.node=10000000;
set hive.hadoop.supports.splittable.combineinputformat=true;
set mapred.min.split.size.per.rack=10000000;
set hive.exec.compress.output=true;
set hive.merge.mapfiles=true;
set hive.merge.mapredfiles=true;
set hive.merge.size.per.task=256000000;
set hive.merge.smallfiles.avgsize=16000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table bs_collect.tsconfirm_transtion partition(c_tano,d_cdate)
select * from bs_collect.tsconfirm where d_cdate<'2017-09-01';
- 資料檔案回寫
set mapred.max.split.size=25000000;
set mapred.min.split.size.per.node=10000000;
set hive.hadoop.supports.splittable.combineinputformat=true;
set mapred.min.split.size.per.rack=10000000;
set hive.exec.compress.output=true;
set hive.merge.mapfiles=true;
set hive.merge.mapredfiles=true;
set hive.merge.size.per.task=256000000;
set hive.merge.smallfiles.avgsize=16000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table bs_collect.tsconfirm partition(c_tano,d_cdate)
select * from bs_collect.tsconfirm_transtion where d_cdate<'2017-09-01';
結果
這是源表的資料檔案數量
這是處理後的
可以看到,檔案數量明顯減少。