hadoop mr + hive数据倾斜解决

数据倾斜是进行大数据计算时常见的问题。主要分为map端倾斜和reduce端倾斜,map端倾斜主要是因为输入文件大小不均匀导致,reduce端主要是partition不均匀导致。

在hive中遇到数据倾斜的解决办法:

一、倾斜原因:map端缓慢,输入数据文件多,大小不均匀

当出现小文件过多,需要合并小文件。可以通过set hive.merge.mapfiles=true来解决。

set hive.map.aggr=true; //map端部分聚合,相当于Combiner,可以减小压力(默认开启)

set hive.groupby.skewindata=true(默认关闭);//有数据倾斜的时候进行负载均衡,当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。

单个文件大小稍稍大于配置的block块的大写,此时需要适当增加map的个数。解决方法:set mapred.map.tasks个数

文件大小适中,但map端计算量非常大,如select id,count(*),sum(case when...),sum(case when...)...需要增加map个数。解决方法:set mapred.map.tasks个数,set mapred.reduce.tasks个数

二、当遇到一个大表和一个小表进行join操作时

解决方法:小表在join左侧,大表在右侧,或使用mapjoin 将小表加载到内存中。然后再对比较大的表进行map操作。

join就发生在map操作的时候,这里的join并不会涉及reduce操作。map端join的优势就是在于没有shuffle,

如:select /*+ MAPJOIN(a) */ 

a.c1, b.c1 ,b.c2 from a join b 

where a.c1 = b.c1; 

三、遇到需要进行join的但是关联字段有数据为null,如表一的id需要和表二的id进行关联,null值的reduce就会落到一个节点上

解决方法1:子查询中过滤掉null值,id为空的不参与关联

解决方法2:用case when给空值分配随机的key值(字符串+rand())

四、不同数据类型关联产生数据倾斜

场景:一张表s8的日志,每个商品一条记录,要和商品表关联。但关联却碰到倾斜的问题。s8的日志中有字符串商品id,也有数字的商品id,类型是string的,但商品中的数字id是bigint的。猜测问题的原因是把s8的商品id转成数字id做hash来分配reduce,所以字符串id的s8日志,都到一个reduce上了,解决的方法验证了这个猜测。

解决方法:把数字类型转换成字符串类型

Select * from s8_log a

Left outer join r_auction_auctions b

On a.auction_id = cast(b.auction_id as string);

五、当HiveQL中包含count(distinct)时

如果数据量非常大,执行如select a,count(distinct b) from t group by a;类型的SQL时,会出现数据倾斜的问题。

解决方法:使用sum...group by代替。如select a,sum(1) from (select a, b from t group by a,b) group by a;

六、join和Group的优化 
2.1 对于普通的join操作,会在map端根据key的hash值,shuffle到某一个reduce上去,在reduce端做join连接操作,内存中缓存join左边的表,遍历右边的表,一次做join操作。所以在做join操作时候,将数据量多的表放在join的右边。 
当数据量比较大,并且key分布不均匀,大量的key都shuffle到一个reduce上了,就出现了数据的倾斜。 

在map端产生join

         mapJoin的主要意思就是,当链接的两个表是一个比较小的表和一个特别大的表的时候,我们把比较小的table直接放到内存中去,然后再对比较大的表格进行map操作。join就发生在map操作的时候,每当扫描一个大的table中的数据,就要去去查看小表的数据,哪条与之相符,继而进行连接。这里的join并不会涉及reduce操作。map端join的优势就是在于没有shuffle,


2.2 对于Group操作,首先在map端聚合,最后在reduce端坐聚合,hive默认是这样的,以下是相关的参数 
· hive.map.aggr = true是否在 Map 端进行聚合,默认为 True 
· hive.groupby.mapaggr.checkinterval = 100000在 Map 端进行聚合操作的条目数目

 

自己总结:

1 map join 开启配置参数

set hive.auto.convert.join = true  设置自动选择mapjoin

set hive.mapjoin.smalltable.filesize = 25000000  大表小标的阈值设置,默认是25m

2 map 预聚合 ( count group by经常用到) 

set hive.map.aggr = true;  设置默认在map端进行聚合;

hive.groupby.mapaggr.checkinterval = 100000; 在map端进行聚合操作的条目数目;

hive.groupby.skewindata = true; 有数据倾斜时在map端进行负载均衡,默认false, 本质是提交了两个job, 第一个job的mr进行负载均衡预处理;

 

hadoop mr过程优化

1 数据输入

1) 合并小文件:在执行mr任务之前将小文件进行合并,大量的小文件会产生大量的map任务,增到map任务装载次数,任务装载比较耗时。

2) 采用CombineTextInputFormat来作为输入,解决输入端大量小文件的场景。

2 map阶段

1)减少溢写spill次数:通过调整环形缓冲区io.sort.mb和sort.spill.percent参数值,增大触发spill内存上限,减少spill次数,从而减少磁盘io。

2)减少合并merge次数:通过调整io.sort.factor参数,增大merge文件数目,减少merge次数,从而缩短mr时间。

3)能应用combine的尽量应用,减少io。应用场景是聚合sum等计算,但如果是求平均值等就不合适了。

3 reduce阶段

1)合理设置map reduce数量: 两个不能太少也不能太多。太少会导致task等待,延长处理时间;太多,会导致map reduce间竞争资源,造成处理超时等错误。

2)设置map reduce共存:调整slowstart.completedmaps参数,使map运行到一定程度之后,reduce也开始运行,减少reduce等待时间。

3)规避使用reduce:因为reduce在用于连接数据集时会产生大量的网络消耗。

4)合理设置reduce端的buffer:默认情况下,当数据大于一定阈值的时候,buffer中的数据会写入磁盘,然后reduce会从磁盘中读取数据,有大量的磁盘IO。既然有这个弊端,可以参数设置,使得buffer中的数据直接输送到reduce, 从而减少IO开销。设置mapred.job.reduce.input.buffer.percent,默认为0.0。当值大于0的时候,会保留制定比例的内存读取buffer数据直接拿给reduce使用。这样一来,设置buffer、读取数据、reduce计算都需要内存,所以需要根据作业运行情况进行设置。

4 IO传输

1)采用数据压缩方式,减少io时间:snappy/lzo压缩编码器。

2)使用sequenceFile二进制文件。

5 数据倾斜问题

1)抽样和范围分区:

通过对原始数据进行抽样得到的结果集来预设分区边界值。

2)自定义分区:根据具体业务中键值的业务背景来自定义分区。

3)Combine:相当于在map端进行reduce,预聚合,尽量减少输出,应用场景是聚合sum等计算,但如果是求平均值等就不合适了。

4)采用map join,避免采用reduce join。

6 常用参数调优

hadoop 小文件优化

1)hadoop Archive

一种文件存档工具,对内依然是多个文件,对外namenode而言只占用一个文件的150byte存储,减少了namenode内存使用。

2) sequence file

由一系列二进制key/value组成,如果key为文件名,value为文件内容,可以将大批小文件合并成一个大文件。

3)CombineFileInputFormat

是一种新的InputFormat,用于将多个文件合并成一个单独的file split, 另外会考虑数据的存储位置。

4) 开启jvm重用

对于大量的小文件job, 开启重用可以减少45%时间。

一个map task运行在一个jvm上,开启重用的话,该map在jvm上运行完毕之后,jvm会继续运行其他map task。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章