hive小文件优化
程序员文章站
2022-07-14 19:57:43
...
小文件产生的原因
- 直接向表里面插入数据
比如我们要往一张表里面写入几行特定的数据,如下,每次运行都会往表里写入一个文件,这种一般生产很少见,因为这种操作一般说在位置的时候采用,如果有也是建议先将所有数据放在一个文件再做batch load的方式,这样就只有一个文件了。
insert into table A values('201','2','北京')
- 通过load方式加载数据
这种操作一般是将某一个或者一批文件上传到一个表里面,本地的多少个文件映射到hive就有多少个文件,运行语句如下
load data local inpath '/data/emp.csv' overwrite into emp
- 通过SQL往里面加载数据
这种方式生产最常见,也是最容易产生小文件的方式,insert导入数据时会启动MR任务,MR中reduce有多少个就输出多少个文件
文件数量=reduceTask数量*分区数
如果说我们常见的Map side join 这种场景,那么输出的文件个数
文件数量=mapTask数量*分区数
insert overwrite table A select emp_id,ename from B
小文件过多产生的影响
- 首先对底层存储HDFS来说,HDFS本身就不适合存储大量小文件,小文件过多会导致namenode元数据特别大,占用太多内存,严重影响HDFS的性能
- 对 hive 来说,在进行查询时,每个小文件都会当成一个块,启动一个Map任务来完成,而一个Map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的Map数量是受限的。
如何解决小文件过多
- 使用hive自带的concatenate命令,自动合并小文件
注意:concatenate只针对RCFILE或者ORC文件类型
语句大概如下:默认合并的文件大小和mapreduce.input.fileinputformat.split.minsize这个参数有关系,一般集群都是256MB
set mapred.max.split.size=268435456;
set mapred.min.split.size.per.node=268435456;
set mapred.min.split.size.per.rack=268435456;
set mapred.job.queue.name=queue_9999_01;
alter table cx_ods_safe.event partition(dt="20200101",projectid="weixin123") concatenate;
- 调整参数减少Map数量
- 设置map输入合并小文件的相关参数:
#执行Map前进行小文件合并
#CombineHiveInputFormat底层是 Hadoop的 CombineFileInputFormat 方法
#此方法是在mapper中将多个文件合成一个split作为输入
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; -- 默认
#每个Map最大输入大小(这个值决定了合并后文件的数量)
set mapred.max.split.size=268435456; -- 256M
#一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=268435456; -- 256M
#一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
set mapred.min.split.size.per.rack=268435456; -- 256M
- 设置map输出和reduce输出进行合并的相关参数:
#设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true;
#设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true;
#设置合并文件的大小
set hive.merge.size.per.task = 268435456; -- 256M
#当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge
set hive.merge.smallfiles.avgsize=16000000; -- 16M
- 启用压缩:
# hive的查询结果输出是否进行压缩
set hive.exec.compress.output=true;
# MapReduce Job的结果输出是否使用压缩
set mapreduce.output.fileoutputformat.compress=true;
- 减少Reduce数量
#reduce 的个数决定了输出的文件的个数,所以可以调整reduce的个数控制hive表的文件数量,
#hive中的分区函数 distribute by 正好是控制MR中partition分区的,
#然后通过设置reduce的数量,结合分区函数让数据均衡的进入每个reduce即可。
#设置reduce的数量有两种方式,第一种是直接设置reduce个数
#这种会有一个风险点就是随着这个数量的增加,reduce的个数需要调整
set mapreduce.job.reduces=10;
#第二种是设置每个reduce的大小,
#Hive会根据数据总大小确定reduce个数
set hive.exec.reducers.bytes.per.reducer=5120000000; -- 默认是1G,设置为5G
#执行以下语句,将数据均衡的分配到reduce中
set mapreduce.job.reduces=10;
insert overwrite table A partition(dt)
select * from B
distribute by rand();
解释:如设置reduce数量为10,则使用 rand(), 随机生成一个数 x % 10 ,
这样数据就会随机进入 reduce 中,防止出现有的文件过大或过小