Hive小文件合并

HDFS中的文件、目录和块都映射为一个对象,存储在NameNode服务器内存中,通常占用150个字节。 如果有1千万个文件,就需要消耗大约3G的内存空间。如果是10亿个文件呢,简直不可想象。所以我们要了解一下,hadoop 处理小文件的各种方案,然后选择一种适合的方案来解决本的小文件问题。

此外,HDFS读写小文件时也会更加耗时,因为每次都需要从NameNode获取元信息,并与对应的DataNode建立连接。对于MapReduce程序来说,小文件还会增加Mapper的个数,job作为一个独立的jvm实例,每个job只处理很少的数据,其开启和停止的开销可能会大大超过实际的任务处理时间,浪费了大量的调度时间。当然,这个问题可以通过使用CombinedInputFile和JVM重用来解决.

参考:
https://blog.csdn.net/WYpersist/article/details/80043816

输入文件合并

输入合并。即在Map前合并小文件。这个方法即可以解决之前小文件数太多,导致mapper数太多的问题;还可以防止输出小文件合数太多的问题(因为mr只有map时,mapper数就是输出的文件个数)。
文件合并失效,且job只有map时,map的个数就是文件个数;通过控制map大小控制map个数,以控制输出文件个数。
set hive.hadoop.supports.splittable.combineinputformat=true; 开关
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; 执行Map前进行小文件合并

set mapred.max.split.size=2048000000; 2G 每个Map最大输入大小
set mapred.min.split.size.per.node=2048000000; 一个节点上split的至少的大小 ,决定了多个data node上的文件是否需要合并
set mapred.min.split.size.per.rack=2048000000; 一个交换机下split的至少的大小,决定了多个交换机上的文件是否需要合并
MR-Job 默认的输入格式 FileInputFormat 为每一个小文件生成一个切片。
CombineFileInputFormat 通过将多个“小文件”合并为一个”切片”(在形成切片的过程中也考虑同一节点、同一机架的数据本地性),让每一个 Mapper 任务可以处理更多的数据,从而提高 MR 任务的执行速度。

解读:CombineFileInputFormat类:https://www.cnblogs.com/skyl/p/4754999.html

输出文件合并

输出合并。即在输出结果的时候合并小文件
动态分区好用,但是会产生很多小文件。原因就在于,假设初始有N个mapper,最后生成了m个分区,最终会有多少个文件生成呢?答案是N*m,是的,每一个mapper会生成m个文件,就是每个分区都会对应一个文件,这样的话你算一下。所以小文件就会成倍的产生。
怎么解决这个问题,通常处理方式也是像上面那样,让数据尽量聚到少量reducer里面。但是有时候虽然动态分区不会产生reducer,但是也就意味着最后没有进行文件合并,我们也可以用distribute by rand()这句来保证数据聚类到相同的reducer。
参考:https://www.iteblog.com/archives/1533.html

但是如果是多层分区呢,且二层分区数据量差异很大,虽然也可以使用上面的方式但仍然有可能不均匀,此时需要扩展,采用如下方式distribute by if(productType in ('h','f','t'),floor(rand() * 10),1)

其他合并方式,配置参数set hive.merg.xxxx在文件合并 和 压缩 并存时会失效,即只对text或者seq文件生效,对压缩格式如orc等会不适用,就有些鸡肋
参考:https://meihuakaile.github.io/2018/10/19/hive%E5%B0%8F%E6%96%87%E4%BB%B6%E5%90%88%E5%B9%B6/

上面的方式只是解决使用和规避问题,如果是已经有很多小文件,那么只有压缩一条路可走了,问题就转变为如何更快更优的解决各种格式的压缩问题了。