离线计算的数据倾斜问题
数据倾斜就是指我们在计算数据的时候,数据的分散度不够,导致大量的数据集中到一台或者几台机器上,这些数据的计算速度远远低于平均计算速度,导致整个计算过程很慢。
Hadoop中的数据倾斜主要表现在任务在Reducer阶段会长时间停留在大概99%处不能结束。这时如果仔细查看日志就会发现有一个或者多个reducer执行过程报oom错误或者container加载失败,这时基本可以判断本次离线计算任务的输入数据可能存在数据倾斜问题。
导致数据倾斜的原因可能有很多种,常见的原因如下
某些sql语句本身有问题。比如sql种含有distinct的计算。
Key分布不均匀。比如在两张表join过程中,其中一张表的key分布不均匀。
业务数据本身有问题。比如业务数据join的id分布不均匀,或者id为null指的比重较大,都会引起数据倾斜。
建表时考虑不周。比如同为type字段,其中一张表类型为int,另外一张表类型为string。
列举了一些常用的导致数据倾斜的场景
场景1:当一个大表和一个小表join时,如果小表的key比较集中,将会引起大表中的数据被分发到一个或者少数几个reducer任务中,导致数据分布不均匀。
场景2:在group by时,如果分组的维度太小,维度的值分布不均匀,将导致数据分布不均匀。
场景3:当大表与大表关联时,在关联的条件字段中,其中一个表的空值、null值过多,将导致数据分布不均匀。
解决方案
1. 调整参数
可以通过修改hive.map.aggr和hive.groupby.skewindata参数同时配置为true,在mapper端进行聚合操作,当发生数据倾斜时进行负载均衡。所发生成的查询计划会有两个MR任务。在第一个MR任务中,Mapper阶段的输出结果集合会被随机分布到Reducer阶段中,每个Reducer都会进行部分聚合操作,并输出结果。这样处理的结果是相同的key可以被分发到不同的reducer中,从而达到负载均衡的目的。在第二个MR任务中,Mapper根据第一个MR任务预处理后的数据结果再按照key输出给reducer,这个过程可以保证相同的key被分布到同一个reducer中。经过这两轮MR任务最后完成最终的聚合操作。相关的参数设置如下
hive.map.aggr=true
hive.groupby.skewindata=true
2. 优化sql语句
使用mapjoin:让小的维度表(建议在20000条记录以下)先写入内存,并按照顺序扫描大表完成join。这种方式比较使用于大表和小表的join。
空值优化:可以将空值的key变成一个字符串加上随机数,把倾斜的数据分布到不同的Reducer中,也可以对空值进行单独处理,然后再和其他非空值的计算结果进行合并。
group by优化:采用sum()结合group by的方式替换count(distinct)来完成计算。
3. 特殊情况特殊处理
在业务逻辑优化效果不太友好的情况下,有些时候可以将倾斜的数据单独拿出来处理,最后在union,为了方便理解,下面列举几个业务场景来进行说明。
案例1:空值产生的数据倾斜问题
场景:比如在日志中,通常会发生信息丢失的问题。假如日志中的order_id存在丢失情况,如果将其中的order_id和订单表的order_id关联,就会出现数据倾斜。
解决方法1:order_id为空值的则不参与关联,用union all 合并数据,如代码所示。
select * from log a
join orders b
on a.order_id = b.order_id
and a.order_id is not null
union all
select * from log a
where a.order_id is null;
解决方法2:为空值分配一个随机值,如代码所示。
select *
from log a
left outer join orders b
on case when a.order_id is null then cast(rand()*10000 as bigint) else a.order_id end
= b.order_id;
总结:解决方法2比方法1的执行效率更高,不但I/O少了,而且作业数也少了,在解决方法1中log读取两次,job数是2;在解决方法2中job数是1。这种优化适合由于无效id(比如-99、’’、null等无效字符组合)产生的倾斜问题。把空值的key变成一个字符串加上随机数,就能把倾斜的数据分布在不同的Reducer中,从而解决数据倾斜问题。
关河令·秋阴时晴渐向暝
【作者】周邦彦 【朝代】宋
秋阴时晴渐向暝,变一庭凄冷。伫听寒声,云深无雁影。
更深人去寂静,但照壁孤灯相映。酒已都醒,如何消夜永!