coalesce没起作用_ci


  • coalesce提高性能!
  • coalesce不要滥用!

上面俩货是网上最常见的对coalesce的评价,话也不多说,直接让我们一起来站在源码的角度,分析一下coalesce这个东西

首先一起来看一下coalesce的定义:


def coalesce(numPartitions: Int, shuffle: Boolean = false,
               partitionCoalescer: Option[PartitionCoalescer] = Option.empty)
              (implicit ord: Ordering[T] = null)
      : RDD[T] = withScope {
    if (shuffle) {
      /** 就是一个重分区的函数,打散分区的操作. */
      val distributePartition = ...
      new CoalescedRDD(
        new ShuffledRDD(mapPartitionsWithIndex(distributePartition), ...).values
    } else {
      new CoalescedRDD(this, numPartitions, partitionCoalescer)
    }
  }


上面有几点需要注意的:

  1. 若shuffle为false:生成一个CoalescedRDD,并依赖于当前RDD
  2. 反之,先生成一个依赖于当前RDD的ShuffledRDD,然后再生成 CoalescedRDD
  3. distributePartition是打散分区的、 partitionCoalescer是说怎样合并分区,这俩先不做关注

----------------------------------------------------------------------------------------------另外,还需要明确一个重要的点:DAGScheduler创建Task的数量取决于Stage的最后一个RDD的分区数,可以参考下面的源码(DAGScheduler划分Stage调用过程中会调用createShuffleMapStage方法):


def createShuffleMapStage(shuffleDep: ShuffleDependency[_, _, _], jobId: Int): ShuffleMapStage = {
    val rdd = shuffleDep.rdd
    val numTasks = rdd.partitions.length
    ...
    val stage = new ShuffleMapStage(
      id, rdd, numTasks, ...)
    ...
    stage
  }


可以看到,stage封装了最后一个rdd的分区数量`numTasks`

-----------------------------------------------------------------------------------------------

我们所说的coalesce提升性能,是在某些特定场景下可能会提高计算速度比如filter.coalesce,因为filter之后可能会出现数据不均匀的现象,通过 coalesce合并分区达到提高利用率等效果。同样的,这种情况下coalesce使用不当也可能造成OOM等意外,原因正是上面说到的每个stage任务数量取决于coalesce的传的数字,本来这个stage需要2000个task可以正常运行,但是由于coalesce存在,硬生生的把整个stage的task数量降低了,每个task计算数据成倍的增长,进而导致影响性能甚至是crash。

说完如何影响性能,其实这里也可以解释repartition与他的区别:

repartition其实就是令shuffle为true,可以看到,这种情况下,生成的是2个RDD,第一个ShuffleRDDthis产生宽依赖,其实也就是将coalesce操作推到下一个stage的最开始了,意思就是下一个Stage爱多少个task就多少,别影响我上一个Stage的task数量就行,这也是减少分区的另一种办法。最后还是,存在即合理,两种算子合理的运用才能写出优质的Spark程序。