Spark的flatMap算子引发的数据倾斜问题

Spark的flatMap算子引发的数据倾斜问题

问题背景

Spark中有时候会用到flatMap算子来处理数据,flatMap把序列打平,即将每一条记录变成多条记录。这个算子在数据量大的时候经常会发生数据倾斜问题,你会发现一旦原始数据记录到达亿级、十亿级甚至百亿级时,这个算子会非常令人头疼,任务一直卡在最后一个或者几个task上面,毫无进展,GC日志会显示“not enough memory”,最后任务因为内存出现“Executor heartbeat timed out after XXXX ms”的提示,然后任务就这样挂了。

数据示例

这里列出一个示例,我们有一个大数据表,假设在HBASE里面每条记录是KV形式存储,key是物品ID,ID唯一;value是物品的属性。假设有28个属性,用feature1、feature2…feature30来表示,这28个属性由逗号分隔拼接成一个字符串,如果该物品在某个属性上面没有值,用空 ( null ) 表示;如果某个属性可以有多个值,多个值之间用分号 ( ; ) 表示。以下给出示例

keyvalue
12345613,2,1,19,199,119199,440300,782,5,null,null,null,null,null,null,1,121;117;107;103;106;122;111;102;114;105,005,50908,1.0,null,null,13,null,null,19;22;28,1901;2802;2204,null
12345711,2,1,22,-22,122000,500000,345,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null
12345811,2,1,12,-12,112000,310000,233,null,null,null,null,null,null,null,null,104;103;112;106;122;111;102;115;101,null,null,null,50102,null,12,40404,null,null,null,34
12345912,2,1,19,199,119199,440300,255,null,55,63,15,141,342,1,null,null,null,null,null,null,null,null,null,null,null,null,33
计算目标

这里需要统计每个特征的覆盖情况和每个特征值的占比情况。例如,feature( i ) 不是多值特征,有3个取值 ‘A’、‘B’、‘C’,即feature( i ) ∈ { ‘A’、‘B’、‘C’},假设物品ID一共10亿,在 feature( i )上面有值的ID一共有8亿,则覆盖率为80%;假设 feature( i ) 取值为 ‘A’ 的有4亿,取值为 ‘B’ 的有3亿,取值为 ‘C’ 有1亿,则占比分别为50%、37.5%、12.5%。

问题原因

我们读取全量数据之后,对每个value进行拆分(split)操作,拆分后对每个特征判断是否为空,不为空的话和ID一起形成一个pair对 ( ID , feature( i )_value ),后面的feature(i)表示特征名,value表示特征值,使用下划线连接起来;如果是多值特征,这个特征会变成多条记录出现 ( ID , feature( i )_value1 )、( ID , feature( i )_value2 ) . . .

这样一条记录经过 flatMap 操作之后可能会变成了28条甚至更多条记录,后面 reduceByKey 进行累计统计。

假设 flatMap 变成了28条记录,那么记录量从10亿瞬间变成了280亿,由于特征分布的不平衡性,有些分区的数据量会远远大于其他分区,造成实际的数据倾斜。

解决方式
  • 增加分区的数量,相当于增加并行度,这是一种粗暴的方式,可以缓解下,但是对于大数据量的情况用处基本不大

  • 调整参数的一些值,使用重试机制,一旦某些任务超时,及时启动重试机制, 一面某个任务卡死

  • 分阶段进行 reduceByKey,可能某个pair对总记录非常多,那么在 reduceByKey 的时候就会把所有相同的key拉取到一个partition上面,这个partition内存就会爆掉。有一个办法可以大幅缓解,在 reduceByKey 之前将pair对加上一个随机数,例如将ID前面加入一个随机数,然后使用 “_” 分隔,这样就打散了原有的 key,如果某些 key 对应的记录特别多,可以再在前面加一个随机数和一个分隔符,这样后面两次 reduceBykey 来处理,但是如果数据量超大的话,因为传输的数据变大了,需要的内存也相应的变多,不建议添加超过2个随机数

  • 如果以上方法都失效了,说明数据量是在是太大了,那么我们换种方式解决问题。使用for循环,每个特征 reduceBeKey 一次,串行的方式进行,这样虽然时间长些,但是任务可以正常跑完

  • 如果连for循环单个特征都无法跑过,说明内存设置的确需要增大了,调大executor的数量和内存放大 1.2 - 1.5 倍基本可以解决

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值