我正在编写一个预处理应用程序,除其他转换和操作外,该应用程序最后将数据集排序,然后再将其写入HDFS。一个新请求要求我对数据集进行重复数据删除,因此我希望在排序的一个阶段中执行此操作。我的理解是,为了有效地执行重复数据删除,排序是必要的(也许我是错的,没有做太多研究,看起来很自然)。
由于某些原因(输出架构中的MapType
列),我首先在distinct
之前的阶段对sort
进行了测试,以为以后可以摆脱MapType
列以便将它们合并在一起。
发生的事情是,跳过了排序的第二阶段,就好像数据集已经被排序一样。这对我来说很有意义,但在文档(AFAIK)的任何地方均不受支持,我也不知道预期的行为是否稳定(我不想将其推向生产阶段只是为了意识到自己突然之间做两个昂贵的阶段:sort
和distinct
两者)。任何人都对sort
和/或distinct
的实施方式有更多见解?
答案 0 :(得分:7)
在Spark中,distinct
和一般所有聚合操作(例如groupBy
)不对数据进行排序。我们可以使用explain
函数轻松地进行检查。
// Let's generate a df with 5 elements in [0, 4[ to have at least one duplicate
val data = spark.range(5).select(floor(rand() * 4) as "r")
data.distinct.explain
== Physical Plan ==
*HashAggregate(keys=[r#105L], functions=[])
+- Exchange hashpartitioning(r#105L, 200)
+- *HashAggregate(keys=[r#105L], functions=[])
+- *Project [FLOOR((rand(7842501052366484791) * 5.0)) AS r#105L]
+- *Range (0, 10, step=1, splits=2)
HashAggregate
+ Exchange
表示对元素进行哈希处理和混洗,以便具有相同哈希值的元素位于同一分区中。然后,将具有相同散列的元素进行比较并取消重复。因此,该数据不会在处理后进行排序。让我们检查一下:
data.distinct.show()
+---+
| r|
+---+
| 0|
| 3|
| 2|
+---+
现在解决您对性能的关注。如果您在重复数据删除之后进行排序,则会发生这种情况。
data.distinct.orderBy("r").explain
== Physical Plan ==
*Sort [r#227L ASC NULLS FIRST], true, 0
+- Exchange rangepartitioning(r#227L ASC NULLS FIRST, 200)
+- *HashAggregate(keys=[r#227L], functions=[])
+- Exchange hashpartitioning(r#227L, 200)
+- *HashAggregate(keys=[r#227L], functions=[])
+- *Project [FLOOR((rand(-8636860894475783181) * 4.0)) AS r#227L]
+- *Range (0, 5, step=1, splits=2)
我们可以看到数据经过重新整理以进行重复数据删除(Exchange hashpartitioning
),并再次经过重新整理以进行排序(Exchange rangepartitioning
)。那很贵。这是由于以下事实:排序需要按范围重新排序,以便同一范围内的元素最终位于同一分区中,然后可以对其进行排序。但是,在删除重复数据之前,我们可以变得更聪明,更聪明:
data.orderBy("r").distinct.explain
== Physical Plan ==
*HashAggregate(keys=[r#227L], functions=[])
+- *HashAggregate(keys=[r#227L], functions=[])
+- *Sort [r#227L ASC NULLS FIRST], true, 0
+- Exchange rangepartitioning(r#227L ASC NULLS FIRST, 200)
+- *Project [FLOOR((rand(-8636860894475783181) * 4.0)) AS r#227L]
+- *Range (0, 5, step=1, splits=2)
仅剩一个交换。实际上,spark知道按范围重新排列后,重复的元素就位于同一分区中。因此,它不会触发新的随机播放。