我有一个包含下一个架构的数据框:
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')
我有两个问题,如果有人可以指出错误的步骤,我们将不胜感激。
对于10亿甚至1000亿条记录:实现目标的最佳做法是什么(为每个id_1找到最高得分的id_2)?我已经尝试了5000万和100M的记录,并且通过改组数据获得了更好的结果(这与Holden Karau所说的相反)。我已经id_1
.repartition(X," id_1")
然后 reduceByKey ,速度更快。为什么呢?
为什么DataFrame API比RDD API慢几倍?我哪里错了?
感谢。
答案 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 条记录。
我相信如果我将处理200B记录,shuffle会导致一些IO故障,最好不要使用某些列的重新分区(因为洗牌)但我不知道如何在没有它的情况下提高速度。不幸的是,StackOverflow无法给我正确的答案。 :(
谢谢你们提出有趣的建议!