欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

hive小文件优化

程序员文章站 2022-07-14 19:57:43
...

小文件产生的原因

  1. 直接向表里面插入数据
    比如我们要往一张表里面写入几行特定的数据,如下,每次运行都会往表里写入一个文件,这种一般生产很少见,因为这种操作一般说在位置的时候采用,如果有也是建议先将所有数据放在一个文件再做batch load的方式,这样就只有一个文件了。
insert into table A values('201','2','北京')
  1. 通过load方式加载数据
    这种操作一般是将某一个或者一批文件上传到一个表里面,本地的多少个文件映射到hive就有多少个文件,运行语句如下
load data local inpath '/data/emp.csv' overwrite into emp
  1. 通过SQL往里面加载数据
    这种方式生产最常见,也是最容易产生小文件的方式,insert导入数据时会启动MR任务,MR中reduce有多少个就输出多少个文件
    文件数量=reduceTask数量*分区数
    如果说我们常见的Map side join 这种场景,那么输出的文件个数
    文件数量=mapTask数量*分区数
insert overwrite table A select emp_id,ename from B

小文件过多产生的影响

  1. 首先对底层存储HDFS来说,HDFS本身就不适合存储大量小文件,小文件过多会导致namenode元数据特别大,占用太多内存,严重影响HDFS的性能
  2. 对 hive 来说,在进行查询时,每个小文件都会当成一个块,启动一个Map任务来完成,而一个Map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的Map数量是受限的。

如何解决小文件过多

  1. 使用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;
  1. 调整参数减少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;
  1. 减少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 中,防止出现有的文件过大或过小