Spark 之 算子调优(二)

6,207 阅读4分钟

这是我参与更文挑战的第22天,活动详情查看:更文挑战

算子调优四:filter与coalesce的配合使用

  在Spark任务中我们经常会使用filter算子完成RDD中数据的过滤,在任务初始阶段,从各个分区中加载到的数据量是相近的,但是一旦进过filter过滤后,每个分区的数据量有可能会存在较大差异,如图所示:

image.png

  • 根据图中信息我们可以发现两个问题:
    • 每个partition的数据量变小了,如果还按照之前与partition相等的task个数去处理当前数据,有点浪费task的计算资源;

    • 每个partition的数据量不一样,会导致后面的每个task处理每个partition数据的时候,每个task要处理的数据量不同,这很有可能导致数据倾斜问题。

  如上图所示,第二个分区的数据过滤后只剩100条,而第三个分区的数据过滤后剩下800条,在相同的处理逻辑下,第二个分区对应的task处理的数据量与第三个分区对应的task处理的数据量差距达到了8倍,这也会导致运行速度可能存在数倍的差距,这也就是数据倾斜问题。

  • 针对上述的两个问题,我们分别进行分析:

    • 针对第一个问题,既然分区的数据量变小了,我们希望可以对分区数据进行重新分配,比如将原来4个分区的数据转化到2个分区中,这样只需要用后面的两个task进行处理即可,避免了资源的浪费。

    • 针对第二个问题,解决方法和第一个问题的解决方法非常相似,对分区数据重新分配,让每个partition中的数据量差不多,这就避免了数据倾斜问题。

那么具体应该如何实现上面的解决思路?我们需要coalesce算子。

  repartition与coalesce都可以用来进行重分区,其中repartition只是coalesce接口中shuffle为true的简易实现,coalesce默认情况下不进行shuffle,但是可以通过参数进行设置。

  • 假设我们希望将原本的分区个数A通过重新分区变为B,那么有以下几种情况:
    • A > B(多数分区合并为少数分区)

      1. A与B相差值不大:  此时使用coalesce即可,无需shuffle过程。
      2. A与B相差值很大:  此时可以使用coalesce并且不启用shuffle过程,但是会导致合并过程性能低下,所以推荐设置coalesce的第二个参数为true,即启动shuffle过程。
    • A < B(少数分区分解为多数分区)

      1. 此时使用repartition即可,如果使用coalesce需要将shuffle设置为true,否则coalesce无效。
        我们可以在filter操作之后,使用coalesce算子针对每个partition的数据量各不相同的情况,压缩partition的数量,而且让每个partition的数据量尽量均匀紧凑,以便于后面的task进行计算操作,在某种程度上能够在一定程度上提升性能。

  注意:local模式是进程内模拟集群运行,已经对并行度和分区数量有了一定的内部优化,因此不用去设置并行度和分区数量。

算子调优五:reduceByKey预聚合

  reduceByKey相较于普通的shuffle操作一个显著的特点就是会进行map端的本地聚合,map端会先对本地的数据进行combine操作,然后将数据写入给下个stage的每个task创建的文件中,也就是在map端,对每一个key对应的value,执行reduceByKey算子函数。reduceByKey算子的执行过程如图所示:

  • 使用reduceByKey对性能的提升如下:
    • 本地聚合后,在map端的数据量变少,减少了磁盘IO,也减少了对磁盘空间的占用;
    • 本地聚合后,下一个stage拉取的数据量变少,减少了网络传输的数据量;
    • 本地聚合后,在reduce端进行数据缓存的内存占用减少;
    • 本地聚合后,在reduce端进行聚合的数据量减少。

  基于reduceByKey的本地聚合特征,我们应该考虑使用reduceByKey代替其他的shuffle算子,例如groupByKey。reduceByKey与groupByKey的运行原理如图所示:

groupByKey原理 image.png

reduceByKey原理 image.png

  根据上图可知,groupByKey不会进行map端的聚合,而是将所有map端的数据shuffle到reduce端,然后在reduce端进行数据的聚合操作。由于reduceByKey有map端聚合的特性,使得网络传输的数据量减小,因此效率要明显高于groupByKey。