(一)对小文件进行合并,是行至有效的提高调度效率的方法


动态分区插入数据插入阶段,产生大量的小文件,最多产生 [任务数(map或者reduce) * 分区数] 个文件(实际会小于这个数值)

小文件的影响:
    1.从Hive的角度看,小文件会开很多map,一个map开一个JVM去执行,所以这些任务的初始化,启动,执行会浪费大量的资源,严重影响性能。
    2.在HDFS中,每个小文件对象约占150byte,如果小文件过多会占用大量内存(主要是元数据)。这样NameNode内存容量严重制约了集群的扩展。
    Hadoop并不擅长对小型文件的储存,原因取决于Hadoop文件系统的文件管理机制,Hadoop的文件存储的单元为一个块(block),block的数据存放在集群中的datanode节点上,
    由namenode对所有datanode存储的block进行管理。namenode将所有block的元数据存放在内存中,以方便快速的响应客户端的请求。那么问题来了,不管一个文件有多小,
    Hadoop都把它视为一个block,大量的小文件,将会把namenode的内存耗尽。

1.源头上控制
①.少用动态分区,用时记得按distribute by分区
使用distribute by 语句将数据聚集成按分区分布(若分区数据量分布差异不是很大,优先推荐该办法)

// 根据年份和气温对气象数据进行排序,以确保所有具有相同年份的行最终都在一个reducer分区中
From record2
select year, temperature
distribute by year
sort by year asc, temperature desc;

②.减少reduce数量 (调节参数)
调整hive.exec.reducers.bytes.per.reducer,(若调整后出现倾斜,sql添加 distribute by rand() )

2.对已经存在的小文件:
①.hadoop archive方法
Archive可以把多个文件归档成为一个文件,换个角度来看,Archive实现了文件的元数据整理,但是,归档的文件大小其实没有变化,只是压缩了文件的元数据大小。
②.重建表,建表时减少reduce数量。
③.通过参数进行调节,在hive操作前,设置map/reduce端的相关参数,如下:
设置map输入合并小文件的相关参数:

-- 每个Map最大输入大小,决定合并后的文件数
set mapred.max.split.size=256000000;
-- 一个节点上split的至少的大小 ,决定了多个data node上的文件是否需要合并
set mapred.min.split.size.per.node=100000000;
-- 一个交换机下split的至少的大小,决定了多个交换机上的文件是否需要合并
set mapred.min.split.size.per.rack=100000000;
-- 执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

设置map输出和reduce输出进行合并的相关参数:

hive.merge.mapfiles 在map-only job后合并文件,默认true
hive.merge.mapredfiles 在map-reduce job后合并文件,默认false
hive.merge.size.per.task 合并后每个文件的大小,默认256000000
hive.merge.smallfiles.avgsize 平均文件大小,是决定是否执行合并操作的阈值,默认16000000

(二)sql语句上:
1.join原则:小的文件放 join 左边,左边的会加载到内存
2.数据量较大的情况下,慎用 count(distinct)
3.通过分区(时间、地点): 查询的时候指定分区,会大大减少在无用数据上的扫描, 同时也非常方便数据清理