vlambda博客
学习文章列表

处理 HDFS 上的过多小文件的问题

HDFS 上文件和目录数量过多,会对 namenode 的压力也越来越大,需要对 HDFS 上的小文件进行合并处理,但并非所有文件都可以合并,一般是针对 Hive 表映射在 HDFS 的文件进行合并来减少文件数量,下面是小编整理出来的 3 个处理方法:

方法一
使用官方工具 parquet-tools合并指定的parquet 文件

使用 parquet-tools 对多个 parquet 文件进行合并,使用方法:

# 合并 HDFS 上的 parquet 文件
hadoop jar parquet-tools-1.9.0.jar merge /tmp/a.parquet /tmp/b.parquet
# 合并本地的 parquet 文件
java -jar parquet-tools-1.9.0.jar merge /tmp/a.parquet /tmp/b.parquet


方法二
Create Table As Select (CTAS),即用 hive 把数据从源表(含大量小文件)查出并插入到一张临时表,所有数据插入到临时表后,源表和临时表的表明互换即可。注意,你需要给 hive 会话添加下面的配置来控制小文件合并的条件:

# 控制每个任务合并小文件后的文件大小(默认256000000,256MB):
set hive.merge.size.per.task=2048000000;
# 告诉 hive 什么样的文件属于小文件(默认16000000,小于16MB):
set hive.merge.smallfiles.avgsize=512000000;
# 是否合并Map的输出文件(默认true):
set hive.merge.mapfiles=true;
# 是否合并Reduce的输出文件(默认false):
set hive.merge.mapredfiles=true;
另外,如果 hive 表是分区表,你可以写脚本每个分区遍历合并,但是每个分区合并都需要提交一个 mr job 明显效率不高。更好的做法是在 hive 会话开启动态分区,一次读取多个分区或者全表进行合并(视表的记录数量和集群内存选择吧),下面是开启动态分区的配置:

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


方法三
如果 hive 表使用 orc 格式,则可以使用 hql 进行小文件合并。看看官方的原文描述:

bash ALTER TABLE table_name [PARTITION (partition_key = 'partition_value' [, ...])] CONCATENATE; If the table or partition contains many small RCFiles or ORC files, then the above command will merge them into larger files. In case of RCFile the merge happens at block level whereas for ORC files the merge happens at stripe level thereby avoiding the overhead of decompressing and decoding the data.
小编的 hive 表没有使用 orc 格式,因为 impala 2.12.0 不支持 orc 格式,不过 impala 3.1 开始就支持 orc 了。

另外提醒:
sqoop 的增量同步会产生非常多的小文件,最好把方法二的“控制小文件合并的条件”的配置加上。