背景
Hive运行原理小文件问题相关参数
Hive参数的查看与Hive小文件相关的参数
设置Map输入合并小文件的参数 hive 小文件合并方案
背景Hive任务调优是一个大数据工程师日常工作和面试过程中经常会遇到的问题,经过各种学习与调研,发现网上的各种资料大同小异,而且对各种参数的解释并不是很详细清晰,故写此文以供自己复习与同行参考,后续将持续更新。
Hive运行原理Hive是linkedIn开发的一个基于Hadoop的数仓工具,开发它的目的主要是为了方便数据分析、业务方更加快速地使用数据,毕竟写MapReduce程序门槛较高而且比较繁琐,而写SQL对大多数人来说,非常容易上手。
Hive是SQL解析引擎,其原理就是将SQL转换为Map/reducer job然后在Hadoop执行。所以我们在进行Hive调优的过程中很有必要对MapReduce的原理及框架有个非常清晰的认知。对新手来说,推荐《Hadoop权威指南》。
对Hive的运行原理及架构,推荐Hive官方文档,认真看完此文后,将会对Hive的工作流程十分了解:
小文件问题相关参数 Hive参数的查看https://cwiki.apache.org/confluence/display/hive/design
在Hive 命令行里面可以输入set 命令查看所有与Hive相关的参数及其默认值,如图所示:
如果想查看查看单个参数的默认值是多少,可以用如下命令:
set mapred.max.split.size;
结果如下:
提前声明一下,我们在执行Hive任务前根据自己的需求设置合适的参数并不会在HDFS上合并小文件,只是会影响到Map和Reduce的数量,原始需要处理的数据的小文件个数还是不会改变。意思就是如果不设置参数,100个小文件就会产生100个Map,而如果设置了参数,那么可能就是1个Map或者2个Map来处理这个数据(这一点很多博客都没有提及的,导致对这个问题一直存在认知偏差,这个点将在后文测试证明)。
设置参数合并小文件的好处:
设置Map输入合并小文件的参数对 hive 来说,在进行查询时,每个小文件都会当成一个块,启动一个Map任务来完成,而一个Map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的Map数量是受限的。
在网上搜索资料调研时,发现很多文章都会提到下面这四个参数,下面我将详细介绍一下:
#执行Map前进行小文件合并 #CombineHiveInputFormat底层是 Hadoop的 CombineFileInputFormat 方法 #此方法是在mapper中将多个文件合成一个split作为输入 set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; -- 默认参数 1)设置了这个参数,只是说明有这个能力进行合并,并不代表在运行过程中,会将所有的文件合并成一个, 具体合并的情况需要根据实际情况来综合评估。 #每个Map最大输入大小(这个值决定了合并后文件的数量) set mapred.max.split.size=256000000; -- 256M 1)即一个Map Task能够处理的最大数据量,比如总共的小文件量 500M, 这个时候就需要拆分成两个Map来进行处理。 2)既然有最大那么一定有最小,默认最小 mapred.min.split.size=1, 这个参数也是可以决定Map的数量的。 *下面这两个参数的大小关系是 mapred.min.split.size.per.rack >= mapred.min.split.size.per.node 如果不满足这个条件,运行的时候也将会报错提示你需要修改。 #一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并) set mapred.min.split.size.per.node=100000000; -- 100M #一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并) set mapred.min.split.size.per.rack=100000000; -- 100M
测试
- 原始数据
stu 表共4行数据,对应HDFS上面4个不同的路径。查看hive默认参数:
根据这个默认参数,我们查看一下运行效果
执行下面这个命令
select count(1) from stu;
结果产生了2个Map,这个说明确实进行了合并处理,然后我们看看HDFS文件路径,仍旧是4个小文件,验证了此前我的声明,HDFS文件是不会减少的,只是Map数量会有影响。之所以是2个Map,这个我估计是Hive物理计划生成器的内部的代码根据各个参数以及文件在DataNode上的分布综合考量得出的这么一个结论,是各种执行策略共同叠加的效果(例如:mapreduce有个策略就是移动计算而不是移动文件,意思就是大数据集群的网络资源是非常宝贵且拥塞的,与其将一个大文件传到一个节点上进行处理,不如就直接在这个大文件所在节点分配资源进行计算)。当然实际情况更加复杂一些,后序将持续研究。
4. Question: 上面这个查询,2个Map我都嫌多,毕竟只有这么一点点数据,几十个bit,一个Map进行 *** 作完全够用。那么我们应该如何设置参数呢?
这个时候想到了这个参数:
set mapred.min.split.size=10000; # 每个Map最小处理数据量,给它加大一些
这个时候执行阶段是不是就只产生一个Map呢?
我们拭目以待:
结果很失败,还是产生了2个Map,这就令人匪夷所思,明明设置了参数,理论上一个Map最小处理数据量应该10000bit,那4个小文件数据量加起来还不够呢,应该合并啊。所以这个时候就告诉我们一个现象了,并不是设置了一个参数就一定会生效,实际可能会考虑其它情况。在这里我猜测是优先级的问题,因为Hive的参数总共得有上千个,这么多参数条件,不可能每条都满足。
那我们试试其他两个参数:
set mapred.min.split.size.per.node=10000; set mapred.min.split.size.per.rack=10000;
结果还算是达到了预期效果,参数生效。为什么?
一个节点上的Map处理的数据量必须要是大于10000。
通过下面这个命令可以查看元数据信息,文件三副本块位于哪些节点上。
hadoop fsck /apps/hive/warehouse/stu -files -blocks -locations
Map任务依赖NodeManager产生,所以这个四个块将会移动到这个节点合并然后进行处理。3个副本具体选择哪个副本目前这方面我也不太了解,再往下面就更加深入了。
- per.node per.rack 这两个参数,哪个优先级更高。
per.rack 似乎并不一定需要满足。而per.node确实强制需要的。
我的集群只有一个机架,如果pre.node=1 per.rack = 10000,结果还是有两个Map。
这个情况怎么解释?这个问题期待大神能给出答案。我再仔细研究研究。
对于存储格式为ORC格式的hive表,可以使用concatenate。
#对于非分区表 alter table A concatenate; #对于分区表 alter table B partition(day=20201224) concatenate;
还有一种比较简单的方式就是创建一个和原Hive表结构一样的表
create table new_table like old_table; insert into new_table select * from old_table;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)