1   数据倾斜的表现

1.1 hadoop中的数据倾斜表现

Ø  Map处理正常完成100%,有一个Reduce卡在99.99,一直不能完成。

Ø  各种container报错OOM。

Ø  异常的Reducer读写的数据量极大,远超其它正常的Reducer处理平均值,同时出现任务被kill的诡异表现

1.2  hive中数据倾斜

Ø  分组(group by)维度少,维度值太多,造成处理某个值的Reduce耗时;

Ø  关联(join on)不匹配,造成处理挤压耗时;

1.3      Spark中的数据倾斜

Spark中的数据倾斜包括Spark Streaming和Spark Sql,表现主要有下面几种:

Ø  Executor lost,OOM,Shuffle过程出错;

Ø  Driver OOM;

Ø  单个Executor执行时间特别久,整体任务卡在某个阶段不能结束;

Ø  正常运行的任务突然失败;

2 数据倾斜产生原因

以Spark、Hive使用场景为例,进行数据加工运算,常会涉及group by、join on、count distinct等操作,这些操作都需要进行分区、排序、合并、归并,也就是我们常说的Shuffle,相同key值会被拉到一个或几个Reduce节点上,容易发生单点计算问题,导致数据倾斜。数据倾斜原因有以下几方面:

1)       key分布不均匀

2)       业务数据本身的特性

业务数据本省在不同区域办理量,参与活动人数,总消费就存在不均衡。再比如营销活动进行促销,某个时间段省会城市的流量增长10倍,而其他地市数据量基本不变,当我们统计不同地市的流量情况,进行Group操作,就直接数据倾斜了。

3)       SQL语句存在数据倾斜,未对空置进行处理。

3  数据倾斜解决方案

很多数据倾斜问题,究其根本是与业务强相关,如何理解业务特点,做好数据架构层面的设计和流程处理尤为重要。如好的数据预处理,完备的异常值过滤,是解决数据倾斜的关键;首先从业务角度分析对异常数据进行研判,是否可以在预处理环节对异常值进行处理,提高数据质量规避数据倾斜;再次从流程处理上,可考虑对分布不均匀的数据单独计算;最后从程序层面对KEY值分布不均,大量同一KEY值进行一次hash,将数据随机打散让它的并行度变大,再汇聚。

3.1 join倾斜优化

3.1.1 大表关联小表

方案一:通过人为的方式放大维表数据量,大表的数据被系统根据关联字段分到更多的reduce中进行处理,从而减少执行时间。维表放大的倍数根据大表和小表的数据量决定,对于亿级数据,一般可以把维表的数据量放大十万甚至百万级别。

Ø  优化前

select

a.user_id,

a.disc_id,

b.disc_name

from tc_ub_user_disc_d a

join ( select disc_name from td_pr_disc t1 where is_main = 1 ) b

on a.disc_id = b. disc_id

注:主表总数据量约10+亿,从表按条件过滤后不到500条记录

主表关联的key字段disc_id分布不均匀,部分disc_id对应2千万+记录

运行时长60+分钟

Ø  优化后

select

a.user_id,

a.disc_id,

b.disc_name

from tc_ub_user_disc_d a

join ( select

concat(t1.disc_id,t2.segment_no) disc_union_seg,t1.disc_name

from td_pr_disc t1 join

td_pr_segment_00_99 t2 on 1=1

 where t1.is_main = 1 ) b

on concat(a.disc_id,substr(a.user_id,length(a.user_id)-2,2)) = b. disc_union_seg

注:t2表为00-99的100条记录,与t1表笛卡尔关联,生成100倍的小表记录

    运行时长20分钟以内

 方案二:使用mapjoin将小表加载到内存中

select /*+ MAPJOIN(a) */ a.c1, b.c1 ,b.c2 from a join b where a.c1 = b.c1;

 

3.1.2     大表关联大表

join时,假设左表(left_table)存在大量的空值,空值将聚集到一个reduce上。由于left_table 存在大量的记录,无法使用mapjoin。此时可以使用 coalesce(left_table.key, rand()*9999)将key为空的情况下赋予随机值,来避免空值集中造成数据倾斜。

如:

Ø  优化前

select a.user_id,

b.group_type

from tc_sc_user_info_d a   --主表总数据量约3亿+

left join tc_gc_group_d b    --从表中group_id为主键

on a.group_id = b.group_id

Ø  优化后

select a.user_id,

b.group_type

from tc_sc_user_info_d a

left join tc_gc_group_d b 

on coalesce(a.group_id, rand()*9999) = b.group_id

注:b.group_id不在0~9999范围

 

3.2  count(distinct)倾斜优化

由于Distinct操作的存在,数据无法在Map端的Shuffle阶段根据Group By先做一次聚合操作,减少传输的数据量,而是将所有的数据都传输到Reduce端,当Key的数据分发不均匀时,就会导致Reduce端倾斜,特别当多个Distinct同时出现在一段SQL代码中时,数据会被分发多次,不仅会造成数据膨胀N倍,也会把倾斜现象放大N倍,可以考虑使用sum…group by代替。

Ø  优化前: 只有一个reduce,先去重再count负担比较大

select c1,count(distinct c2) from t group by c1

 

Ø  优化后:

设置该任务的每个job的reducer个数为3个。Hive默认-1,自动推断。 set mapred.reduce.tasks=3; // 启动两个job,一个负责子查询(可以有多个reduce),另一个负责count(1)

select c1,sum(1)

from (select c1,c2 from t group by c1,c2)

group by c1;

 

3.3  group by倾斜优化优化

在进行类型统计的时候,某种类型的数据量特别多,其他的类型特别少。当按照类型进行group by的时候,会将相同的group by字段的reduce任务需要的数据拉取到同一个节点进行聚合,而当其中某一组的数据量过大时,会出现其他组的计算已经完成而这个reduce还没有计算完成,其它的节点一直等待这个节点的任务执行完成,所以会一直看到map 100% reduce99%的情况。

解决方法:

set hive.map.aggr=true;

set hive.groupby.skewindata=true;

 

ü  配置说明:

hive.map.aggr=true 这个配置表示在map端开启聚合计算;

hive.groupby.skewindata=true,选项设定为true,系统生成两个MR Job。第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,相同的分组Key被分发到不同的Reduce中,从而实现负载均衡。第二个MR Job再根据预处理的数据结果按照Group By Key分布到reduce中,这个过程可以保证相同的key被分到同一个reduce中,最后完成聚合操作。

 

3.4  系统参数层面倾斜优化

Hadoop平台MR计算也存在数据倾斜问题,主要受数据块的数量和数据块大小影响,有以下几种情况,可通过调优系统参数来解决。

1)       存在大量的小文件,启动了太多的Map

解决方法:对小文件进行合并,降低map数。

可以通过set hive.merge.mapredfiles=true参数

2)       输入数据存在大块和小块

如一个大文件128M,还有1000个小文件,每 个1KB。

解决方法:任务输入前做文件合并,将众多小文件合并成一个大文件。

可通过set hive.merge.mapredfiles=true解决;

3)       单个文件大小稍稍大于配置的block块的大小

需要适当增加map的个数。

解决方法:set mapred.map.tasks的个数;

4)       文件大小适中

map端计算量非常大,如:select id,count(*),sum(case when…),sum(case when …)…需要增加map个数。

解决方法:set mapred.map.tasks个数,