针对reduceByKey

时间:2017-05-13 19:37:23

标签: python apache-spark pyspark spark-dataframe bigdata

我有一个包含下一个架构的数据框:

root
 |-- id_1: long (nullable = true)
 |-- id_2: long (nullable = true)
 |-- score: double (nullable = true)

数据如下:

+----+----+------------------+
|id_1|id_2|score             |
+----+----+------------------+
|0   |9   |0.5888888888888889|
|0   |1   |0.6166666666666667|
|0   |2   |0.496996996996997 |
|1   |9   |0.6222222222222221|
|1   |6   |0.9082996632996633|
|1   |5   |0.5927450980392157|
|2   |3   |0.665774107440774 |
|3   |8   |0.6872367465504721|
|3   |8   |0.6872367465504721|
|5   |6   |0.5365909090909091|
+----+----+------------------+

目标是为每个id_1找到最高得分的id_2。也许我错了,但是......只需要创建配对的RDD:

root
 |-- _1: long (nullable = true)
 |-- _2: struct (nullable = true)
 |    |-- _1: long (nullable = true)
 |    |-- _2: double (nullable = true)

+---+----------------------+
|_1 |_2                    |
+---+----------------------+
|0  |[9,0.5888888888888889]|
|0  |[1,0.6166666666666667]|
|0  |[2,0.496996996996997] |
|1  |[9,0.6222222222222221]|
|1  |[6,0.9082996632996633]|
|1  |[5,0.5927450980392157]|
|2  |[3,0.665774107440774] |
|3  |[8,0.6872367465504721]|
|3  |[8,0.6872367465504721]|
|5  |[6,0.5365909090909091]|
+---+----------------------+

并按键减少最大值。像

这样的东西
paired_rdd.reduceByKey(lambda x1, x2: max(x1, x2, key=lambda x: x[-1]))

或与DataFrame API相同(没有配对的rdd):

original_df.groupBy('id_1').max('score')

我有两个问题,如果有人可以指出错误的步骤,我们将不胜感激。

  1. 对于10亿甚至1000亿条记录:实现目标的最佳做法是什么(为每个id_1找到最高得分的id_2)?我已经尝试了5000万和100M的记录,并且通过改组数据获得了更好的结果(这与Holden Karau所说的相反)。我已经id_1

    完成了重新分区

    .repartition(X," id_1")

    然后 reduceByKey ,速度更快。为什么呢?

  2. 为什么DataFrame API比RDD API慢几倍?我哪里错了?

  3. 感谢。

2 个答案:

答案 0 :(得分:4)

您的用例是窗口聚合函数的完美用例。试一试,看看它与RDD reduceByKey的对比情况。

有时不是关于基于RDD的管道是否比基于DataFrame的管道更快,而是基于RDF的管道与另一管道的表现力如何。基于DataFrame的管道几乎总是比基于RDD的替代方案更具表现力(并且可能在长期内更易于维护)。

(我正在使用Scala并将代码转换为Python作为家庭练习)

scala> dataset.show
+----+----+------------------+
|id_1|id_2|             score|
+----+----+------------------+
|   0|   9|0.5888888888888889|
|   0|   1|0.6166666666666667|
|   0|   2| 0.496996996996997|
|   1|   9|0.6222222222222221|
|   1|   6|0.9082996632996633|
|   1|   5|0.5927450980392157|
|   2|   3| 0.665774107440774|
|   3|   8|0.6872367465504721|
|   3|   8|0.6872367465504721|
|   5|   6|0.5365909090909091|
+----+----+------------------+

import org.apache.spark.sql.expressions.Window
val byId_1 = Window.partitionBy("id_1")
original_df.select($"id_1", max() over byId_1)
scala> dataset.
  select($"id_1", $"id_2", $"score", max("score") over byId_1 as "max_score").
  filter($"score" === $"max_score").
  distinct.  // <-- id_1 == 3 is duplicated
  sort("id_1").
  show
+----+----+------------------+------------------+
|id_1|id_2|             score|         max_score|
+----+----+------------------+------------------+
|   0|   1|0.6166666666666667|0.6166666666666667|
|   1|   6|0.9082996632996633|0.9082996632996633|
|   2|   3| 0.665774107440774| 0.665774107440774|
|   3|   8|0.6872367465504721|0.6872367465504721|
|   5|   6|0.5365909090909091|0.5365909090909091|
+----+----+------------------+------------------+

请注意,默认情况下,DataFrame使用spark.sql.shuffle.partitions 200我上周有一个案例,其中大部分分区(以及因此任务)为空,导致数千个任务等待执行这是没有用的和烧毁的CPU周期。我们从几小时到几秒钟。

了解您的数据以及如何对其进行分区是优化Spark查询的第一步,无论是使用RDD API还是数据集API编写的。

答案 1 :(得分:2)

感谢Jacek提出的有趣建议。

我已经在4 * c4.8xlarge服务器上执行了一些测试(128个核心,192GB RAM,我希望32个工作人员和分区= 128对此设置有利)。 已使用的数据集包含 1,368,598,093 条记录。

  1. &#34;窗口&#34;解决方案 - 大约 43分钟并产生大约31GB的随机播放(15.4GB shuffle write和15.4GB shuffle read)。见第25阶段。 enter image description here
  2. 使用reduceByKey解决方案,无需按ID重新分区 - 40分钟和8.4MB随机播放(4.2MB shuffle write和4.2MB shuffle read)参见阶段#22 enter image description here
  3. 获胜者 - reduceByKey 按ID重新分区。 22分钟和15GB随机播放(7.5GB随机播放和7.5GB随机播放)请参阅第24阶段 enter image description here
  4. 我相信如果我将处理200B记录,shuffle会导致一些IO故障,最好不要使用某些列的重新分区(因为洗牌)但我不知道如何在没有它的情况下提高速度。不幸的是,StackOverflow无法给我正确的答案。 :(

    谢谢你们提出有趣的建议!