一、小文件产生原因
hive 中的小文件是向 hive 表中导入数据时产生,所以先看下向 hive 中导入数据的几种方式
1.直接向表中插入数据
insert into table test1 values (1,'aa',67),(2,'bb',87);
2.通过load方式加载数据
load data local inpath '/root/score.csv' overwrite into table test1 -- 导入文件
load data local inpath '/root/score' overwrite into table test1 -- 导入文件夹
使用 load 方式可以导入文件或文件夹,当导入一个文件时,hive表就有一个文件,当导入文件夹时,hive表的文件数量为文件夹下所有文件的数量
3.通过查询方式加载数据
insert overwrite table test1 select id,name,score from test2;
这种方式是生产环境中常用的,也是最容易产生小文件的方式
insert 导入数据时会启动 MR 任务,MR中 reduce 有多少个就输出多少个文件
- 文件数量=ReduceTask数量*分区数
也有很多简单任务没有reduce,只有map阶段,则
- 文件数量=MapTask数量*分区数
每执行一次 insert 时hive中至少产生一个文件,因为 insert 导入时至少会有一个MapTask。
二、小文件过多产生的影响
-
首先对底层存储HDFS来说,HDFS本身就不适合存储大量小文件,小文件过多会导致namenode元数据特别大, 占用太多内存,严重影响HDFS的性能
-
对 hive 来说,在进行查询时,每个小文件都会当成一个块,启动一个Map任务来完成,而一个Map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的Map数量是受限的。
三、怎么解决小文件过多
1. 使用 hive 自带的 concatenate 命令,自动合并小文件
-
先执行一次insert ,插入两条数据
0: jdbc:hive2://hdp101:2181,hdp103:2181,hdp10> insert into table test1 values (1,'aa',67),(2,'bb',87);
- 打印的日志
- HDFS 文件夹下形成的文件
- 执行 insert 三次,test1 表下面会有三个小文件
- 使用 concatenate 进行合并
0: jdbc:hive2://hdp101:2181,hdp103:2181,hdp10> alter table test1 concatenate;
2. 调整参数减少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=256000000; -- 256M
#一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000; -- 100M
#一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
set mapred.min.split.size.per.rack=100000000; -- 100M
-
设置map输出和reduce输出进行合并的相关参数
# 设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true;
# 设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true;
# 设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000; -- 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;
3. 减少Reduce的数量
reduce 的个数决定了输出的文件的个数,所以可以调整 reduce 的个数控制 hive 表的文件数量,hive中的分区函数 distribute by 正好是控制MR中 partition 分区的,然后通过设置 reduce 的数量,结合分区函数让数据均衡的进入每个 reduce 即可。
#设置reduce的数量有两种方式,第一种是直接设置reduce个数
set mapreduce.job.reduces=10;
#第二种是设置每个reduce的大小,Hive会根据数据总大小猜测确定一个reduce个数
set hive.exec.reducers.bytes.per.reducer=5120000000; -- 默认是1G,设置为5G