1、问题背景
最近遇到一个这样的需求,需要将原始数据按照key进行汇总,然后把对应key的value数据按照时间排序进行排序,最后分别对每个key进行相同的value操作,于是遇到了严重的数据倾斜问题。
单个task接收到了单个key对应的大量value数据,造成处理耗时甚至OOM内存溢出或不足,使得整个任务被拖累。
2、数据倾斜解决方案
2.1、初步切分数据
由于需求的特殊性,key对应的value排序后可以进行sliding滑动选取操作,所以最初的单个(key, value)可以通过flatMapValues变成
(key0, value0)
(key0, value1)
(key0, value2)
(key0, value…)
(key0, valueN)
注意:该数据在进行mapValues操作时,key相同的value仍然被收集到同一个task上,所以仍然造成数据倾斜
val originData:RDD[(String, List[String])] = ....
val data = originData.flatMapValues( value =>
value.map(x => (x, x.split("###")(3).toInt)).sortBy(_._2).map(_._1).grouped(1000))
上面的x.split("###")(3).toInt为我的取时间戳字段,grouped(1000)等于sliding(1000, 1000)
2.2、对所有key添加随机前缀
将原始的 key 转化为 key + 随机值(例如Random.nextInt)是一种常用方法
val dataShuffle = data.map(x => {
val prifix = new Random().nextInt(100000)
(prifix + "###" + x._1, x._2)
}).repartition(100)
可以看到上面代码在key前面随机加上了一个10000以内的数字
注意.repartition(100)操作,该操作是为了在后续进行mapValues时,能够将数据均衡发送到每个task上面,也就是100个task
3、运行效果&参考文章
上述优化效果将代码执行时间从小时级别降低到分钟级别