前言

本人集群使用的是cdh5.9.1版本,hive1.1.1,Hadoop2.6。hive中有个数据表有5个分区,每个分区的数据以txt形式存储,大小3G多。想要把当前数据表的数据进行压缩,存储到以orc格式存储的数据表中去。

问题

使用insert语句将数据进行迁移时,发现orc格式的表中的分区文件达到了10多个,每个文件大小平均20MB。HDFS的多个小文件对于namenode的压力很大,而且在执行mapreduce任务时会导致task过多,占用集群资源。

解决

调解一下配置参数,即可达到合并小文件的目的:
1. hive.merge.mapfiles(启用小文件合并 - Map-Only 作业)
在 map-only 作业结束时合并小文件。如启用,将创建 map-only 作业以合并目标表/分区中的文件。
此参数在cdh中默认开启。
2. hive.merge.smallfiles.avgsize(小文件平均大小合并阈值)
当作业的平均输出文件大小小于此属性的值时,Hive 将启动额外的 map-only 作业来将输出文件合并成大文件。仅当 hive.merge.mapfiles 为 true 对map-only 作业执行,当 hive.merge.mapredfiles 为 true 时对 map-reduce 作业执行,以及当 hive.merge.sparkfiles 为 true 时对 Spark 作业执行。
此参数在cdh中默认值为16MB。
3. hive.merge.mapredfiles(启用小文件合并 - Map-Reduce 作业)
在 map-reduce 作业结束时合并小文件。如启用,将创建 map-only 作业以合并目标表/分区中的文件。
此参数在cdh中默认为false。
4. hive.exec.reducers.bytes.per.reducer(每个 Reducer 的 Hive 字节数)
每个 reducer 的大小。如果输入大小为 10GiB 并且该项设置为 1GiB,Hive 将使用 10 个 reducer。
此参数在cdh中默认值为64MB
5. hive.merge.size.per.task(合并后所需的文件大小)
合并后所需的文件大小。应大于 hive.merge.smallfiles.avgsize
此参数在cdh中默认值为256兆字节。

参数配置

根据上述五个参数的解释,可以将hive.merge.mapredfiles = true,即开启mapreduce作业小文件合并;
hive.merge.smallfiles.avgsize = 128Mb。这里解释一下为什么要128MB:
	由于我第一次使用insert语句之后生成的小文件大小平均在20MB左右,而且作业的平均输出大小要小于hive.merge.smallfiles.avgsize的值才能启动文件合并,又因为hdfs的默认块大小为128MB所以将这个值设为128MB。
	hive.exec.reducers.bytes.per.reducer = 1GB
	其余三个参数均使用默认值即可。

结果

经过这些参数的调整之后,新的数据表分区下的文件个数直接减少为两个,大幅度减少了多个小文件的数量。不过这种调参适合于最后的数据落地环节,最好在insert into table前开启即可,不然会影响其他的job执行效率。