(一)小文件产生的原因

1.动态分区插入数据,产生大量的小文件,从而导致map数量剧增。

2.reduce数量越多,小文件也越多(reduce的个数和输出文件是对应的)。

3.数据源本身就包含大量的小文件。

(二)小文件产生的影响

1、首先对底层存储HDFS来说,HDFS本身就不适合存储大量小文件,小文件过多会导致namenode元数据特别大,占用太多内存,严重影响HDFS的性能

2、对 hive 来说,在进行查询时,每个小文件都会当成一个块,启动一个Map任务来完成,而一个Map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的Map数量是受限的。

(三)如何解决

1、从源头控制

尽量不要用textfile,在一定程度上可以减少小文件。

2、用hive的参数控制

(1)set hive.merge.mapfiles = true -- 设置map端输出进行合并
(2)set hive.merge.mapredfiles=true; --设置reduce端输出进行合并
(3)set mapred.max.split.size=256000000;     --决定每个map处理的最大文件大小,B
(4)set mapred.min.split.size.per.node=1;    --节点处理的最小文件大小    
(5)set mapred.min.split.size.per.rack=1     --机架中可以处理的最小文件大小
(6)set mapred.reduce.tasks=-1               --设置reduce数量,这里-1也就是自动计算出需要reduce的个数
(7)set hive.exec.reducers.bytes.per.reducer=256000000 --每个reduce处理的数据量
(8)set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat --map端做combiner操作
(9)set hive.exec.reducers.bytes.per.reducer=256000000 --控制一个job当中会有多少个reduce来处理,依据的是输入文件的总大小
(10)set hive.exec.reducers.max=1009    --控制最大reduce数量
(11)set hive.merge.size.per.task = 256*1000*1000 -- 设置合并文件的大小
(12)set hive.merge.smallfiles.avgsize=16000000 -- 当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge

注意:1、设置map,可能带来集群之间的io,因为可能涉及到文件的合并;

2、设置reduce,则会对最后的结果文件个数造成直接影响;reduce太少,会导致reduce端的数据量太大,影响执行效率,导致整个job都不能结束;reduce太大,则会产生大量的结果文件,合并代价大,占用namenode的内存空间。

根据mapred.max.split.size来确认需要启动多少个map数。

比如文件大小260M,280M,则会启动两个map,剩余的4M,24M先做保留。

再根据mapred.min.split.size.per.node来针对剩下的文件做操作,如果设置为1,则表示剩余的文件自己启动一个map,这里会启动2两个;如果设置为28*1024*1024,则文件会合并成一个,启动一个map。但是正常情况下,不可能正好设置成28*1024*1024,也就是说,还是会产生剩余的文件。

最后根据mapred.min.split.size.per.rack,来决定是否要进行合并,对再次剩余的文件独自产生一个map。

3、若是动态分区插入:使用DISTRIBUTE BY
DISTRIBUTE BY 分区字段,pmod(hash(1000*rand(1)),10)
分区规则是根据分区字段的hash码与reduce的个数进行取余操作后,余数相同的分到一个区。