有关在Spark中加入数据框的问题

时间:2019-03-18 20:08:59

标签: apache-spark pyspark apache-spark-sql pyspark-sql

假设我有两个分区的数据帧:

df1 = spark.createDataFrame(
    [(x,x,x) for x in range(5)], ['key1', 'key2', 'time']
).repartition(3, 'key1', 'key2')

df2 = spark.createDataFrame(
    [(x,x,x) for x in range(7)], ['key1', 'key2', 'time']
).repartition(3, 'key1', 'key2')

(方案1):如果我通过[key1,key2]加入它们,则在每个分区内执行合并操作而不会随机播放(结果数据帧中的分区数相同):

x = df1.join(df2, on=['key1', 'key2'], how='left')
assert x.rdd.getNumPartitions() == 3

(场景2)但是,如果我通过[key1,key2,time]进行混洗操作(结果数据帧中的分区数为200,由spark.sql.shuffle驱动)。分区选项):

x = df1.join(df2, on=['key1', 'key2', 'time'], how='left')
assert x.rdd.getNumPartitions() == 200

同时,按[key1,key2,时间]进行groupby和window操作将保留分区数,并且无需进行随机播放即可完成

x = df1.groupBy('key1', 'key2', 'time').agg(F.count('*'))
assert x.rdd.getNumPartitions() == 3

我不明白这是一个错误还是第二种情况下执行洗牌操作的某些原因?而如果可能的话,我该如何避免洗牌?

2 个答案:

答案 0 :(得分:1)

pyspark和Scala之间的Catalyst Optimizer行为不同(至少使用Spark 2.4)。

我都跑了,得到了两个不同的计划。

实际上,除非您明确声明pyspark,否则您在pyspark中会获得200个分区:

 spark.conf.set("spark.sql.shuffle.partitions", 3)

然后处理3个分区,因此3个保留在pyspark下。

有点奇怪,我本来以为这很普遍。所以人们不断告诉我。它只是显示出来。

通过conf设置参数的pyspark物理计划:

== Physical Plan ==
*(5) Project [key1#344L, key2#345L, time#346L]
+- SortMergeJoin [key1#344L, key2#345L, time#346L], [key1#350L, key2#351L, time#352L], LeftOuter
   :- *(2) Sort [key1#344L ASC NULLS FIRST, key2#345L ASC NULLS FIRST, time#346L ASC NULLS FIRST], false, 0
    :  +- Exchange hashpartitioning(key1#344L, key2#345L, time#346L, 3)
    :     +- *(1) Scan ExistingRDD[key1#344L,key2#345L,time#346L]
    +- *(4) Sort [key1#350L ASC NULLS FIRST, key2#351L ASC NULLS FIRST, time#352L ASC NULLS FIRST], false, 0
       +- Exchange hashpartitioning(key1#350L, key2#351L, time#352L, 3)
         +- *(3) Filter ((isnotnull(key1#350L) && isnotnull(key2#351L)) && isnotnull(time#352L))
             +- *(3) Scan ExistingRDD[key1#350L,key2#351L,time#352L]

答案 1 :(得分:1)

我想能够找出Python和Scala中不同结果的原因。

原因是广播优化。如果在禁用广播的情况下启动spark-shell,则Python和Scala的工作原理相同。

./spark-shell --conf spark.sql.autoBroadcastJoinThreshold=-1

val df1 = Seq(
  (1, 1, 1)
).toDF("key1", "key2", "time").repartition(3, col("key1"), col("key2"))

val df2 = Seq(
  (1, 1, 1),
  (2, 2, 2)
).toDF("key1", "key2", "time").repartition(3, col("key1"), col("key2"))

val x = df1.join(df2, usingColumns = Seq("key1", "key2", "time"))

x.rdd.getNumPartitions == 200

因此,似乎spark 2.4.0无法按需使用@ user10938362建议的开箱即用的优化案例和催化剂优化程序扩展。

顺便说一句。以下是有关编写催化剂优化程序扩展https://developer.ibm.com/code/2017/11/30/learn-extension-points-apache-spark-extend-spark-catalyst-optimizer/

的信息