Hive小文件合并调研

背景
Hive query将运算好的数据写回hdfs(比如insert into语句),有时候会产生大量的小文件,如果不采用CombineHiveInputFormat就对这些小文件进行操作的话会产生大量的map task,耗费大量集群资源,而且小文件过多会对namenode造成很大压力。所以Hive在正常job执行完之后,会起一个conditional task,来判断是否需要合并小文件,如果满足要求就会另外启动一个map-only job 或者mapred job来完成合并



参数解释
hive.mergejob.maponly (默认为true)
如果hadoop版本支持CombineFileInputFormat,则启动Map-only job for merge,否则启动  MapReduce merge job,map端combine file是比较高效的做法


hive.merge.mapfiles(默认为true)
正常的map-only job后,是否启动merge job来合并map端输出的结果


hive.merge.mapredfiles(默认为false)
正常的map-reduce job后,是否启动merge job来合并reduce端输出的结果,建议开启


hive.merge.smallfiles.avgsize(默认为16MB)
如果不是partitioned table的话,输出table文件的平均大小小于这个值,启动merge job,如果是partitioned table,则分别计算每个partition下文件平均大小,只merge平均大小小于这个值的partition。这个值只有当hive.merge.mapfiles或hive.merge.mapredfiles设定为true时,才有效


hive.exec.reducers.bytes.per.reducer(默认为1G)
如果用户不主动设置mapred.reduce.tasks数,则会根据input directory计算出所有读入文件的input summary size,然后除以这个值算出reduce number
   reducers = (int) ((totalInputFileSize + bytesPerReducer - 1) / bytesPerReducer);
   reducers = Math.max(1, reducers);
   reducers = Math.min(maxReducers, reducers);


hive.merge.size.per.task(默认是256MB)
merge job后每个文件的目标大小(targetSize),用之前job输出文件的total size除以这个值,就可以决定merge job的reduce数目。merge job的map端相当于identity map,然后shuffle到reduce,每个reduce dump一个文件,通过这种方式控制文件的数量和大小

MapredWork work = (MapredWork) mrTask.getWork();
if (work.getNumReduceTasks() > 0) {
     int maxReducers = conf.getIntVar(HiveConf.ConfVars.MAXREDUCERS);
     int reducers = (int) ((totalSize +targetSize - 1) / targetSize);
     reducers = Math.max(1, reducers);
     reducers = Math.min(maxReducers, reducers);
     work.setNumReduceTasks(reducers);
}


mapred.max.split.size(默认256MB)
mapred.min.split.size.per.node(默认1 byte)
mapred.min.split.size.per.rack(默认1 byte)

这三个参数CombineFileInputFormat中会使用,Hive默认的InputFormat是CombineHiveInputFormat,里面所有的调用(包括最重要的getSplits和getRecordReader)都会转换成CombineFileInputFormat的调用,所以可以看成是它的一个包装。CombineFileInputFormat 可以将许多小文件合并成一个map的输入,如果文件很大,也可以对大文件进行切分,分成多个map的输入。一个CombineFileSplit对应一个map的输入,包含一组path(hdfs路径list),startoffset, lengths, locations(文件所在hostname list)mapred.max.split.size是一个split 最大的大小,mapred.min.split.size.per.node是一个节点上(datanode)split至少的大小,mapred.min.split.size.per.rack是同一个交换机(rack locality)下split至少的大小通过这三个数的调节,组成了一串CombineFileSplit用户可以通过增大mapred.max.split.size的值来减少Map Task数量



结论
hive 通过上述几个值来控制是否启动merge file job,通常是建议大家都开启,如果是一堆顺序执行的作业链,只有最后一张表需要固化落地,中间表用好就删除的话,可以在最后一个insert into table之前再开启,防止之前的作业也会launch merge job使得作业变慢。


上周还发现目前启动的针对RCFile的Block Merger在某种少见情况下,会生成duplicated files,Hive代码中本身已经考虑到这点,所以会在Merger Task RCFileMergeMapper的JobClose函数中调用Utilities.removeTempOrDuplicateFiles(fs, intermediatePath, dpCtx),  不过不知道为什么没有生效,还会存在重复文件,需要再研究下


Hive是否起merge job是由conditional task在运行时决定的,如果hadoop job或者hive未如预期般执行合并作业,则可以利用github上的file crush工具完成合并,它的原理也是启动一个mapreduce job完成合并,不过目前只支持textfile 和 sequencefile

链接地址:https://github.com/edwardcapriolo/filecrush


原文地址:http://blog.csdn.net/lalaguozhe/article/details/9053645

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hive中的小文件合并慢的原因有以下几点: 1. 数据规模过小:当数据量很小,甚至小于HDFS默认块大小时,会导致生成大量的小文件。这些小文件需要进行合并,但合并操作本身也需要一定的时间。 2. 合并操作的开销:Hive在进行小文件合并时,需要读取和写入大量的数据,这会增加磁盘IO和网络传输的开销。当小文件数量过多时,这些开销会累积导致合并速度变慢。 3. 数据分布不均匀:如果数据分布不均匀,即某些分区或者文件夹中的数据量远远大于其他分区或文件夹,那么在进行小文件合并时,会出现部分节点负载过重的情况,从而导致整体合并速度变慢。 4. 数据压缩方式:如果数据使用了压缩方式存储,那么在进行小文件合并时,还需要进行解压缩和重新压缩的操作,这会增加合并的时间消耗。 为了解决文件合并慢的问题,可以考虑以下几个方法: 1. 调整Hive的参数:可以通过调整Hive的参数来优化小文件合并的性能,比如增大合并操作的并发度、调整合并操作的阈值等。 2. 定期进行合并:可以定期执行合并操作,将小文件合并为大文件,减少小文件的数量,提高查询性能。 3. 数据预处理:在数据写入Hive之前,可以对数据进行预处理,将小文件合并为大文件后再写入Hive,避免生成大量的小文件。 4. 数据分区:合理设计数据分区,将数据均匀地分布在不同的分区中,避免某个分区数据过多导致合并速度变慢。 请注意,以上方法可能需要根据具体情况进行调整和尝试,以达到最佳性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值