Hive数据倾斜及小文件处理
1、数据倾斜原因
数据倾斜主要表现在,map /reduce程序执行时,reduce节点大部分执行完毕,但是有一个或者几个reduce节点运行很慢,导致整个程序的处理时间很长。这是因为某一个key的条数比其他key多很多(有时是百倍或者千倍之多),这条key所在的reduce节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完,reduce完成在99%后一直卡住。
数据倾斜可能会发生在group by、join、count(distinct),当然一些窗口函数中用了partition by一会造成数据倾斜。
2、数据倾斜解决办法
数据倾斜根据不同的产生原因,对应不同的解决办法。
2.1 大表和小表join产生数据倾斜
set hive.exec.reducers.bytes.per.reducer = 1000000000;也就是每个节点的reduce 默认是处理1G大小的数据,如果reduce处理的数据大于该值,启用新的reduce处理。该设置也相当于改变了reduce个数。
大、小表关联时,导致对数据的处理集中在某些key上,造成数据倾斜。多表关联时,将重复关联键少的表(小表)依次放在前面,这样可以触发reduce端更少的操作次数,减少运行时间。
同时可以使用Map Join让小表缓存到内存。在map端完成join过程,从而省略掉redcue端的工作。但是使用这个功能,需要开启map-side join的设置属性:set hive.auto.convert.join=true(默认是false)。
map join 概念:将其中做连接的小表(全量数据)分发到所有 MapTask 端进行 Join,从 而避免了 reduceTask,前提要求是内存足以装下该全量数据。
至于多大的表算“小表”,由参数 hive.mapjoin.smalltable.filesize 控制,默认set hive.mapjoin.smalltable.filesize=25000000(默认值25M)。
2.2 大表与大表join产生数据倾斜
join过程出现倾斜,设置set hive.optimize.skewjoin = true;
set hive.skewjoin.key = skew_key_threshold (default = 100000);该参数设置,join的键对应的记录条数超过这个值则会进行分拆,分配给未达到该值的reduce,值根据具体数据量设置。
2.3 groupby 产生数据倾斜
set hive.map.aggr=true (默认true) 这个配置项代表是否在map端进行聚合,相当于Combiner;
set hive.groupby.skewindata=true;
set hive.groupby.mapaggr.checkinterval=100000;该参数设置,group的键对应的记录条数超过这个值则会进行分拆,值根据具体数据量设置。
2.4 key中含有较多空值或异常值
对异常值赋一个随机值来分散key
3、小文件过多
3.1 小文件如何产生的
- 动态分区插入数据,产生大量的小文件,从而导致map数量剧增;文件个数 = maptask个数 * 分区数;
- reduce数量越多,小文件也越多(reduce的个数和输出文件是对应的);
- 数据源本身就包含大量的小文件。
3.2 小文件影响
- 从Hive的角度看,小文件会开很多map,一个map开一个JVM去执行,所以这些任务的初始化,启动,执行会浪费大量的资源,严重影响性能;
- 如果小文件过多会占用大量内存。这样NameNode内存容量严重制约了集群的扩展。
3.3 解决方法
- 使用Sequencefile作为表存储格式,不要用textfile,在一定程度上可以减少小文件;
- 减少reduce的数量(可以使用参数进行控制);
- 按动态分区插数据时,可以使用DISTRIBUTE BY rand() 将数据随机分配给Reduce,这样可以使得每个Reduce处理的数据大体一致,避免出现有的文件特别大, 有的文件特别小。
设置map输入合并小文件的相关参数:
-- 设置每个Map最大输入大小(这个值决定了合并后文件的数量)
set mapred.max.split.size=256000000;
-- 设置一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
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输出进行合并的相关参数:
-- 设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
-- 设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
-- 设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000
-- 当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
set hive.merge.smallfiles.avgsize=16000000