通过增加分区或任务的数量,将Spark DataFrame的速度提高到RDD转换

时间:2017-05-29 17:18:40

标签: apache-spark apache-spark-sql spark-dataframe rdd

我在尝试将DF转换为RDD时遇到了问题。这个过程中的一个阶段总共使用了200个任务,而在此之前的大多数部分使用了更多,并且我无法理解为什么它使用这个数字以及我是否需要找到一种方法来增加这个以提高性能

该程序使用Spark版本2.1.0,并在我使用250个执行程序的Yarn集群上运行。

这些是DF转换为RDD的行:

val predictionRdd = selectedPredictions
    .withColumn("probabilityOldVector", convertToOldVectorUdf($"probability"))
    .select("mid", "probabilityOldVector")
    .rdd

这导致前面提到的200个任务,如以下屏幕截图中的活动阶段所示。

enter image description here

它基本上只是卡在这里因为我不知道多长时间,其他两个完成的阶段使用了更多的任务。

我尝试过的一件事就是在将其转换为RDD之前执行重新分区:

val predictionRdd = selectedPredictions
    .withColumn("probabilityOldVector", convertToOldVectorUdf($"probability"))
    .select("mid", "probabilityOldVector")
    .repartition(2000)
    .rdd

val avgPredictions = predictionRdd
    .map(row => (row.getAs[String]("mid"), row.getAs[OldVector]("probabilityOldVector")))
    .aggregateByKey(new MultivariateOnlineSummarizer)(
        (agg, v) => agg.add(v),
        (agg1, agg2) => agg1.merge(agg2)
    )
    .map(p => (p._1, p._2.mean))

假设理想情况下,这将导致执行2000个任务。然而,这有一个(对我来说)意想不到的结果。 This图像(与之前相同)显示该部分所属的整个作业。有趣的是,它仍然显示200个任务,并且在将其转换为RDD之前的重新分区的2000个分区在用于挂起的地图阶段的任务数量中可见。

我觉得要提高这部分的速度,我需要增加正在执行的任务的数量,允许它以更少的内存和更少的内存并行运行。

所以我的问题基本上是:

  1. 我是否至少在某种程度上正确理解了这种情况,或者问题完全在某个地方,
  2. 增加正在执行的任务数量也会提高速度,
  3. 如何增加此部分的任务(或分区)数量,或以其他方式增加速度?
  4. 我仍然是Spark的新手,我在更高层次上了解我的方式,但实际的错综复杂仍然无法实现。

    在撰写本文时,我注意到它在大约1.3小时后显示出一些进展,看似简单。

    以下是执行者和任务的聚合指标的一小部分:

    Executor ID  Address  Task Time  Total Tasks  Failed Tasks  Killed Tasks  Succeeded Tasks  Shuffle Read Size / Records  Shuffle Write Size / Records  Shuffle Spill (Memory)  Shuffle Spill (Disk)
    1            -        1.4 h      1            0             0             1                1810.3 MB / 8527038          2.1 GB / 2745175              5.9 GB                  1456.3 MB 
    10           -        0 ms       0            0             0             0                1808.2 MB / 8515093          0.0 B / 1839668               5.9 GB                  1456.7 MB 
    

    Index  ID     Attempt  Status   Locality Level  Executor ID / Host                 Launch Time          Duration  Scheduler Delay  Task Deserialization Time  GC Time  Result Serialization Time  Getting Result Time  Peak Execution Memory  Shuffle Read Size / Records  Write Time  Shuffle Write Size / Records  Shuffle Spill (Memory)  Shuffle Spill (Disk)  Errors
    0      19454  0        RUNNING  PROCESS_LOCAL   197 / worker176.hathi.surfsara.nl  2017/05/29 17:23:37  1.5 h     0 ms             0 ms                       3.8 min  0 ms                       0 ms                 3.1 GB                 1809.9 MB / 8525371                      0.0 B / 1839667               5.9 GB                  1456.0 MB       
    1      19455  0        SUCCESS  PROCESS_LOCAL   85 / worker134.hathi.surfsara.nl   2017/05/29 17:23:37  1.5 h     42 ms            8 s                        3.2 min  0 ms                       0 ms                 6.0 GB                 1808.3 MB / 8519686          5 s         2.1 GB / 2742924              5.9 GB                  1456.3 MB       
    

    DAG

    以下是上下文的其他几个屏幕截图,作为链接添加,以免使这篇文章过长:

    第45和46阶段在47之前同时运行。源代码的主要部分可以在GitHub上的this代码段中查看。它会加载以前经过培训的CrossValidatorModel,其中包含一个包含五个步骤的管道:CharNGramCountVectorizerIDFRandomForestClassifierIndexToString。它预测了大约5.5亿个文本片段的200个类的概率,最大长度为550个字符。然后将这些预测按输入类组合在一起,然后进行平均。

1 个答案:

答案 0 :(得分:2)

您可以使用以下方式设置任务数量:

      val spConfig = (new SparkConf).setMaster("local[*]").setAppName("MoviesRec")
  // Spark UI available at port 4040.. check here also
  spark = SparkSession.builder().appName("Movies").config(spConfig)
    .config("spark.ui.enabled", true)
      .config("spark.sql.shuffle.partitions", "100")

在日志中,您将获得以下内容:[Stage 9:======================================================> (98 + 2) / 100]
200是数据帧的默认值。 您也可以在SparkUI上的localhost:4040上查看它,检查分区数,每个分区的大小以及您拥有的总RAM。
在我的桌面上,如果我将默认的分区数从200减少到8(我拥有的核心数),那么它真的很快。但是,如果partition = nr的内核可能过于简单,因为分区代表一个数据块(默认为64MB)。所以人们应该走上阶梯,直到至少使用整个记忆。