火花联接性能:多列与单列

时间:2019-10-08 13:01:55

标签: apache-spark apache-spark-sql

如果我在[a,b,c]中有df1列和[a,b,c]中有df2列,并且还有d列,则在两个地方d=concat_ws('_', *[a,b,c])之间存在性能差异

  1. df1.join(df2, [a,b,c])
  2. df1.join(df2, d)

2 个答案:

答案 0 :(得分:1)

不能以回答问题,因为答案取决于数据框的详细信息。

联接的性能在很大程度上取决于以下问题:执行联接需要多少改组。如果联接的两侧都被同一列划分,联接将更快。通过查看联接的执行计划,您可以看到分区的效果。

我们用列df1df2ab创建两个DataFrames cd

val sparkSession = ...
sparkSession.conf.set("spark.sql.autoBroadcastJoinThreshold", -1)
import sparkSession.implicits._
val cols = Seq("a","b","c")
def createDf = (1 to 3).map(i => (i,i,i)).toDF(cols:_*).withColumn("d", concat_ws("_", cols.map(col):_*))
val df1 = createDf
val df2 = createDf

df1df2看起来都一样:

+---+---+---+-----+
|  a|  b|  c|    d|
+---+---+---+-----+
|  1|  1|  1|1_1_1|
|  2|  2|  2|2_2_2|
|  3|  3|  3|3_3_3|
+---+---+---+-----+

当我们按列d对两个DataFrame进行分区并将该列用作连接条件时

df1.repartition(4, col("d")).join(df2.repartition(4, col("d")), "d").explain()

我们得到执行计划

== Physical Plan ==
*(3) Project [d#13, a#7, b#8, c#9, a#25, b#26, c#27]
+- *(3) SortMergeJoin [d#13], [d#31], Inner
   :- *(1) Sort [d#13 ASC NULLS FIRST], false, 0
   :  +- Exchange hashpartitioning(d#13, 4)
   :     +- LocalTableScan [a#7, b#8, c#9, d#13]
   +- *(2) Sort [d#31 ASC NULLS FIRST], false, 0
      +- ReusedExchange [a#25, b#26, c#27, d#31], Exchange hashpartitioning(d#13, 4)

通过d对两个DataFrame进行分区,但通过abc进行联接

df1.repartition(4, col("d")).join(df2.repartition(4, col("d")), cols).explain()

导致执行计划

== Physical Plan ==
*(3) Project [a#7, b#8, c#9, d#13, d#31]
+- *(3) SortMergeJoin [a#7, b#8, c#9], [a#25, b#26, c#27], Inner
   :- *(1) Sort [a#7 ASC NULLS FIRST, b#8 ASC NULLS FIRST, c#9 ASC NULLS FIRST], false, 0
   :  +- Exchange hashpartitioning(a#7, b#8, c#9, 200)
   :     +- Exchange hashpartitioning(d#13, 4)
   :        +- LocalTableScan [a#7, b#8, c#9, d#13]
   +- *(2) Sort [a#25 ASC NULLS FIRST, b#26 ASC NULLS FIRST, c#27 ASC NULLS FIRST], false, 0
      +- ReusedExchange [a#25, b#26, c#27, d#31], Exchange hashpartitioning(a#7, b#8, c#9, 200)

比第一个计划多Exchange hashpartitioning个。在这种情况下,abc的联接会更慢。

另一方面,如果数据帧按abc进行分区,则联接按ab,{{1} }比c的加入要快。

答案 1 :(得分:1)

我怀疑没有连接的连接会更快,因为仅散列各个字符串而不是连接然后进行散列可能更便宜。前者涉及较少的需要GC的Java对象,但这并不是完整的答案。

请注意,这可能不是查询的性能限制步骤,因此任何一种方法都将同样快。当涉及到性能调整时,最好进行测试而不是在没有数据的情况下进行猜测。

也如上所述,如果输入数据已经正确分区,则使列保持未连接状态将使优化器有机会消除联接上的交换。

df1.join(df2, [a,b,c])
df1.join(df2, d)