spark数据倾斜问题
数据倾斜问题的描述
在Spark中,同一个Stage的不同Partition可以并行处理,而具有依赖关系的不同Stage之间是串行处理的。假设某个Spark Job分为Stage 0和Stage 1两个Stage,且Stage 1依赖于Stage 0,那Stage 0完全处理结束之前不会处理Stage 1。而Stage 0可能包含N个Task,这N个Task可以并行进行。如果其中N-1个Task都在10秒内完成,而另外一个Task却耗时1分钟,那该Stage的总时间至少为1分钟。换句话说,一个Stage所耗费的时间,主要由最慢的那个Task决定。换句话说 木桶能装多少水取决于最短的那个木板.
由于同一个stage的所有task都执行相同的计算逻辑,所有在机器节点性能相同的情况下,不同task消耗的时间主要取决于计算的数据数量的大小,所有如果某一个task的数据量明显大于其他的,那么这种情况就是数据发生了倾斜,
数据倾斜的解决方法
数据倾斜引起的原因很多,所有不能说某一个方法就能解决数据倾斜,其实数据倾斜本身不是问题,即使发生了数据倾斜计算任务一样可以完成并且计算出正确的结果,但是代价是浪费了资源和时间,解决数据倾斜本质上是对计算过程的调优,
解决数据倾斜的第一步应该是定位问题发生的位置,打开spark 任务监控ui找到发生倾斜的task,即明显输入数量大于平均值的task或者用时明显超过平均值的task,接下来就是找到 这个task对应的stage 进而找到对应的spark算子,这样就可以定位到引起数据倾斜的代码,下面就是参考下文中的解决方式解决
引起数据倾斜的原因
在进行shuffle的时候需要把形同的key拉取到某一个节点的task中进行处理,比如聚合操作或者join操作,如果某一个key对应的数据量远大于平均值就发生了数据倾斜
常见数据倾斜的原因和解决方式
- 第一种 ,提高shuffle并行度,给shuffle算子传入一个值 比如比如reduceByKey(1000),可以让原本分配给一个task的多个key分配给多个task,进而减少倾斜问题
- 第二种 对RDD执行reduceByKey等聚合类shuffle算子或者在Spark SQL中使用group by语句进行分组聚合时,采用分阶段聚合(局部聚合+全局聚合),第一次是局部聚合,先给每个key都打上一个随机数,比如10以内的随机数,此时原先一样的key就变成不一样的了,比如(hello, 1) (hello, 1) (hello, 1) (hello, 1),就会变成(1_hello, 1) (1_hello, 1) (2_hello, 1) (2_hello, 1)。接着对打上随机数后的数据,执行reduceByKey等聚合操作,进行局部聚合,那么局部聚合结果,就会变成了(1_hello, 2) (2_hello, 2)。然后将各个key的前缀给去掉,就会变成(hello,2)(hello,2),再次进行全局聚合操作,就可以得到最终结果了,比如(hello, 4)
- 将 reduce join 转换为map join,类似于hive的map join 在小表join大表的时候把小表读入内存在map中完成对每个大表的join,具体的操作是不适用join操作,将数据量较小的rdd直接通过collect算子拉取到Driver端的内存中,然后创建一个broadcast(广播)变量,然后将小的rdd赋值给广播变量,然后对目标rdd执行map算子,在map内部读取广播变量里面小的rdd的全部数据进行计算,及与当前rdd的key进行连接计算,这种方式从根本上避免了shuffle,但是只是用于大表和小表的join的情况
- 采样倾斜key并且拆分join操作之后再union合并到一起,具体操作先用sample算子采样,之后统计一个每个key的数量,找到数据量比较大的那几个key,将这几个key对应的数据单独拆分出一个rdd,之后给每个key打上n以内随机数的前缀,之后把需要join的rdd中的对应的key也拆分出一个新的rdd,然后把每个key对应的数据膨胀为原来的n倍,注意这里的n对应n以内随机数的n,并且这些膨胀后的数据前面顺序加上0-n,之后对这两个rdd进行join,因为已经将原来key拆分成了n分,所以会分散到多个task中去执行join,再这之后再将原有的普通rdd照常join就好了,再两个join执行完之后 union 合并到一起