一:程序层面:
比如说在Hive中,经常遇到count(distinct)操作,这样会导致最终只有一个Reduce任务。
我们可以先group by,再在外面包一层count,就可以了。比如计算按用户名去重后的总用户量:
// 优化前 只有一个reduce,先去重再count负担比较大:
select name,count(distinct name)from user;
//优化后
// 设置该任务的每个job的reducer个数为3个。Hive默认-1,自动推断。
set mapred.reduce.tasks=3;
// 启动两个job,一个负责子查询(可以有多个reduce),另一个负责count(1):
select count(1) from (select name from user group by name) tmp;
二:从业务和数据上解决数据倾斜
很多数据倾斜都是在数据的使用上造成的。我们举几个场景,并分别给出它们的解决方案。
数据分布不均匀:
前面提到的“从数据角度来理解数据倾斜”和“从业务计角度来理解数据倾斜”中的例子,其实都是数据分布不均匀的类型,这种情况和计算平台无关,我们能通过设计的角度尝试解决它。
有损的方法:
找到异常数据,比如ip为0的数据,过滤掉
无损的方法:
对分布不均匀的数据,单独计算
先对key做一层hash,先将数据随机打散让它的并行度变大,再汇集
数据预处理
三:MR解决数据倾斜具体方法
(1):combiner函数
思想:提前在map进行combine,减少传输的数据量
在Mapper加上combiner相当于提前进行reduce,即把一个Mapper中的相同key进行了聚合,减少shuffle过程中传输的数据量,以及Reducer端的计算量。
如果导致数据倾斜的key 大量分布在不同的mapper的时候,这种方法就不是很有效了。
(2):导致数据倾斜的key 大量分布在不同的mapper
局部聚合加全局聚合。
第一次在map阶段对那些导致了数据倾斜的key 加上1到n的随机前缀,这样本来相同的key 也会被分到多个Reducer中进行局部聚合,数量就会大大降低。
第二次mapreduce,去掉key的随机前缀,进行全局聚合。
思想:二次mr,第一次将key随机散列到不同reducer进行处理达到负载均衡目的。第二次再根据去掉key的随机前缀,按原key进行reduce处理。
这个方法进行两次mapreduce,性能稍差。
增加Reducer
思想:增加Reducer,提升并行度
JobConf.setNumReduceTasks(int)
实现custom partitioner
思想:根据数据分布情况,自定义散列函数,将key均匀分配到不同Reducer
四:Hive解决数据倾斜具体方法
(1):count(distinct xx)
情形:某特殊值过多
后果:处理此特殊值的reduce耗时;只有一个reduce任务
解决方式:count distinct时,将值为空的情况单独处理,比如可以直接过滤空值的行,在最后结果中加1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
(2):join
情形1:小表与大表join,但较小表key集中
后果:shuffle分发到某一个或几个Reducer上的数据量远高于平均值。想象极端情况,小表的join列全部为一个值,那么shuffle后全部到一个Reducer节点,其他节点无负载。这就是极端的数据倾斜了。
解决方式:mapjoin
情形2:大表与大表join,但是分桶的判断字段0值或空值过多
后果:这些空值/0值都由一个Reducer处理,非常慢
解决方式:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。
五:不同数据类型关联产生数据倾斜
情形:比如用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时。
后果:处理此特殊值的reduce耗时;只有一个reduce任务
默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。
解决方式:把数字类型转换成字符串类型
六: 大表Join大表 - skewjoin
当key值都是有效值时可使用hive配置:
set hive.optimize.skewjoin=true;
指定是否开启数据倾斜的join运行时优化,默认不开启即false。
set hive.skewjoin.key=100000;
判断数据倾斜的阈值,如果在join中发现同样的key超过该值,则认为是该key是倾斜key。
默认100000。一般可以设置成处理的总记录数/reduce个数的2-4倍。
set hive.optimize.skewjoin.compiletime=true;
指定是否开启数据倾斜的join编译时优化,默认不开启即false。
具体来说,会基于存储在原数据中的倾斜key,来在编译时为导致倾斜的key单独创建执行计划,而其他key也有一个执行计划用来join。然后,对上面生成的两个join执行后求并集。因此,除非相同的倾斜key同时存在于这两个join表中,否则对于引起倾斜的key的join就会优化为map-side join。
此外,该参数与hive.optimize.skewjoin之间的主要区别在于,此参数使用存储在metastore中的倾斜信息在编译时来优化执行计划。如果元数据中没有倾斜信息,则此参数无效。一般可将这两个参数都设为true。如果元数据中有倾斜信息,则hive.optimize.skewjoin不做任何操作