Scala:通过第三个关系加入两个DataFrame的更好方法

时间:2016-12-23 08:59:33

标签: scala apache-spark dataframe

我有两个DataFrame,并想要外连接它们。但是连接映射在另一个数据帧中。

现在我正在使用以下方式,它可行,但我希望有更有效的方法,我有> 1,000,000行

val ta = sc.parallelize(Array(
    (1,1,1),
    (2,2,2)
)).toDF("A", "B", "C")

scala> ta.show
+---+---+---+
|  A|  B|  C|
+---+---+---+
|  1|  1|  1|
|  2|  2|  2|
+---+---+---+

val tb = sc.parallelize(Array(
    (2,1)
)).toDF("C", "D")

scala> tb.show
+---+---+
|  C|  D|
+---+---+
|  2|  1|
+---+---+


val tc = sc.parallelize(Array(
    (1,1,1),
    (2,2,2)
)).toDF("D", "E", "F")


scala> tc.show
+---+---+---+
|  D|  E|  F|
+---+---+---+
|  1|  1|  1|
|  2|  2|  2|
+---+---+---+

scala> val tmp = ta.join(tb, Seq("C"), "left_outer")
tmp: org.apache.spark.sql.DataFrame = [C: int, A: int, B: int, D: int]

scala> tmp.show
+---+---+---+----+
|  C|  A|  B|   D|
+---+---+---+----+
|  1|  1|  1|null|
|  2|  2|  2|   1|
+---+---+---+----+

scala> tmp.join(tc, Seq("D"), "outer").show
+----+----+----+----+----+----+
|   D|   C|   A|   B|   E|   F|
+----+----+----+----+----+----+
|null|   1|   1|   1|null|null|
|   1|   2|   2|   2|   1|   1|
|   2|null|null|null|   2|   2|
+----+----+----+----+----+----+

1 个答案:

答案 0 :(得分:1)

正如Umberto所指出的,关于如何提高联盟表现的一个很好的参考是Holden Karau和Rachel Warren的High Performance Spark > Chapter 4. Joins (SQL & Core)

从您的代码的角度来看,按照您的说明运行它或SQL等价物(如下所述)应该会产生大致相同的性能。

// Create initial tables
val ta = sc.parallelize(Array(
    (1,1,1),
    (2,2,2)
)).toDF("A", "B", "C")

val tb = sc.parallelize(Array(
    (2,1)
)).toDF("C", "D")

val tc = sc.parallelize(Array(
    (1,1,1),
    (2,2,2)
)).toDF("D", "E", "F")

// _.createOrReplaceTempView
ta.createOrReplaceTempView("ta")
tb.createOrReplaceTempView("tb")
tc.createOrReplaceTempView("tc") 

// SQL Query
spark.sql("
select tc.D, ta.A, ta.B, ta.C, tc.E, tc.F
  from ta 
  left outer join tb 
    on tb.C = ta.C
  full outer join tc
    on tc.D = tb.D
")

之所以如此,是因为Spark SQL Catalyst Optimizer(如下图所示)采用DataFrame查询并构建优化的逻辑计划。开发了许多物理计划,Spark SQL Engine的成本优化器选择最佳物理计划并生成代码以生成RDD。

Spark SQL Catalyst Optimizer

说到这一点,关键问题是当你处理大量占用大量内存的行时,你必须考虑到分区。例如,如果您可以确保映射DataFrame(tc)与其他DataFrame(tatb)具有相同/相似的分区方案,那么您可以拥有< strong> co-located join (这是{4}}中的图4-3)。

如果您的三个数据框架(tatbtc)的分区都具有不同的分区,则表示您的DataFrame的密钥不会具有1对1分区之间的匹配。也就是说,这将导致 shuffle join (这是{4}}中的图4-2),这可能会更昂贵。

基本上,从您的查询的角度来看,关注点不仅仅是查询本身,更多的是关于DataFrames的分区方案。但在对DataFrame的分区方案进行过多尝试之前,请先试验一下您的查询,看看默认的Spark SQL / DataFrame查询是否能够自行处理分区。