小文件过多问题产生原因
- 源头的数据文件数目本身就很多
- 一般使用动态分区会产生很多小文件(动态分区是根据某个key进行划分分区)
- reduce 个数越多,小文件数目越多。
小文件过多造成的后果
- 从 Hive 的角度看,小文件会开很多 map,一个 map 开一个 JVM 去执行,所以这些任务的初始化、启动、执行会浪费大量的资源,严重影响性能;
- HDFS 存储太多小文件, 会导致 namenode 元数据特别大, 占用太多内存, 制约了集群的扩展;
解决小文件问题思路
-
通过调整参数进行合并
- 每个Map最大输入大小(这个值决定了合并后文件的数量)
set mapred.max.split.size=256000000;
- 一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000;
- 一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
set mapred.min.split.size.per.rack=100000000;
- 执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
-
设置map输出和reduce输出进行合并的相关参数:
- 设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
- 设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
- 设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000
- 当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
set hive.merge.smallfiles.avgsize=16000000
- 每个Map最大输入大小(这个值决定了合并后文件的数量)
-
针对按分区插入数据的时候产生大量的小文件的问题, 可以使用 DISTRIBUTE BY rand() 将数据随机分配给 Reduce,这样可以使得每个 Reduce 处理的数据大体一致.
-
设置每个reducer处理的大小为5个G
set hive.exec.reducers.bytes.per.reducer=5120000000;
- 使用distribute by rand()将数据随机分配给reduce, 避免出现有的文件特别大, 有的文件特别小
insert overwrite table test partition(dt) select * from iteblog_tmp DISTRIBUTE BY rand();
- 使用 Sequencefile 作为表存储格式,不要用 textfile,在一定程度上可以减少小文件
-
-
使用 hadoop 的 archive 归档
-
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 srcpart ARCHIVE PARTITION(ds='2008-04-08', hr='12');
- 对已归档的分区恢复为原文件
ALTER TABLE srcpart UNARCHIVE PARTITION(ds='2008-04-08', hr='12');
- 注意,归档的分区不能够 INSERT OVERWRITE,必须先 unarchive
-