Spark中的广播哈希加入(BHJ)用于完全外连接(外部,完整,全部)

时间:2017-04-25 23:16:41

标签: scala apache-spark apache-spark-sql

如何强制在Spark中使用全局外连接来使用Boradcast Hash Join?以下是代码段:

sparkConfiguration.set("spark.sql.autoBroadcastJoinThreshold", "1000000000")
val Result = BigTable.join(
  org.apache.spark.sql.functions.broadcast(SmallTable),
  Seq("X", "Y", "Z", "W", "V"),
  "outer"
)

我的SmallTable的大小比上面指定的autoBroadcastJoinThreshold小。此外,如果我使用内部,left_outerright_outer联接,我会从DAG可视化中看到联接正在按预期使用BroadcastHashJoin

但是,当我使用“outer”作为联接类型时,spark会因某种未知原因决定使用SortMergeJoin。有谁知道如何解决这个问题?根据我在左外连接中看到的性能,BroadcastHashJoin将有助于我的应用程序多次加速。

2 个答案:

答案 0 :(得分:2)

  由于某些未知原因,

spark决定使用SortMergeJoin。是否   有谁知道如何解决这个问题?

原因: FullOuter(表示任何关键字outerfullfullouter)不支持广播哈希联接(也称为地图)加入)

如何证明这一点?

举个例子:

package com.examples

import org.apache.log4j.{Level, Logger}
import org.apache.spark.internal.Logging
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._

/**
  * Join Example and some basics demonstration using sample data.
  *
  * @author : Ram Ghadiyaram
  */
object JoinExamples extends Logging {
  // switch off  un necessary logs
  Logger.getLogger("org").setLevel(Level.OFF)
   val spark: SparkSession = SparkSession.builder.config("spark.master", "local").getOrCreate;
  case class Person(name: String, age: Int, personid: Int)

  case class Profile(name: String, personId: Int, profileDescription: String)

  /**
    * main
    *
    * @param args Array[String]
    */
  def main(args: Array[String]): Unit = {
    spark.conf.set("spark.sql.join.preferSortMergeJoin", "false")
    import spark.implicits._

    spark.sparkContext.getConf.getAllWithPrefix("spark.sql").foreach(x => logInfo(x.toString()))
    /**
      * create 2 dataframes here using case classes one is Person df1 and another one is profile df2
      */
    val df1 = spark.sqlContext.createDataFrame(
      spark.sparkContext.parallelize(
        Person("Sarath", 33, 2)
          :: Person("KangarooWest", 30, 2)
          :: Person("Ravikumar Ramasamy", 34, 5)
          :: Person("Ram Ghadiyaram", 42, 9)
          :: Person("Ravi chandra Kancharla", 43, 9)
          :: Nil))


    val df2 = spark.sqlContext.createDataFrame(
      Profile("Spark", 2, "SparkSQLMaster")
        :: Profile("Spark", 5, "SparkGuru")
        :: Profile("Spark", 9, "DevHunter")
        :: Nil
    )

    // you can do alias to refer column name with aliases to  increase readablity

    val df_asPerson = df1.as("dfperson")
    val df_asProfile = df2.as("dfprofile")
    /** *
      * Example displays how to join them in the dataframe level
      * next example demonstrates using sql with createOrReplaceTempView
      */
    val joined_df = df_asPerson.join(
      broadcast(df_asProfile)
      , col("dfperson.personid") === col("dfprofile.personid")
      , "outer")
    val joined = joined_df.select(
      col("dfperson.name")
      , col("dfperson.age")
      , col("dfprofile.name")
      , col("dfprofile.profileDescription"))
    joined.explain(false) // it will show which join was used
    joined.show

  }

}

我尝试使用广播提示进行fullouter加入,但框架忽略了,下面的SortMergeJoin是解释计划。 结果:

== Physical Plan ==
*Project [name#4, age#5, name#11, profileDescription#13]
+- SortMergeJoin [personid#6], [personid#12], FullOuter
   :- *Sort [personid#6 ASC NULLS FIRST], false, 0
   :  +- Exchange hashpartitioning(personid#6, 200)
   :     +- *SerializeFromObject [staticinvoke(class org.apache.spark.unsafe.types.UTF8String, StringType, fromString, assertnotnull(input[0, com.examples.JoinExamples$Person, true]).name, true) AS name#4, assertnotnull(input[0, com.examples.JoinExamples$Person, true]).age AS age#5, assertnotnull(input[0, com.examples.JoinExamples$Person, true]).personid AS personid#6]
   :        +- Scan ExternalRDDScan[obj#3]
   +- *Sort [personid#12 ASC NULLS FIRST], false, 0
      +- Exchange hashpartitioning(personid#12, 200)
         +- LocalTableScan [name#11, personId#12, profileDescription#13]
+--------------------+---+-----+------------------+
|                name|age| name|profileDescription|
+--------------------+---+-----+------------------+
|  Ravikumar Ramasamy| 34|Spark|         SparkGuru|
|      Ram Ghadiyaram| 42|Spark|         DevHunter|
|Ravi chandra Kanc...| 43|Spark|         DevHunter|
|              Sarath| 33|Spark|    SparkSQLMaster|
|        KangarooWest| 30|Spark|    SparkSQLMaster|
+--------------------+---+-----+------------------+
  

From spark 2.3 Merge-Sort join是spark中的默认连接算法。   但是,可以使用内部参数调低此值   'spark.sql.join.preferSortMergeJoin'默认为true。

fullouter加入之外的其他情况......如果您不想在任何情况下使用sortmergejoin,您可以设置以下属性。

sparkSession.conf.set("spark.sql.join.preferSortMergeJoin", "false")

这是您不想使用sortmergejoin的代码SparkStrategies.scala (which is responsible & Converts a logical plan into zero or more SparkPlans)的说明。

进出口。注意:

此属性spark.sql.join.preferSortMergeJoin如果为true,则更喜欢通过此PREFER_SORTMERGEJOIN属性对shuffle散列连接进行排序合并连接。

设置false意味着不能选择broadcasthashjo,它也可以是其他任何内容(例如,随机散列连接)。

以下文档位于SparkStrategies.scala,即object JoinSelection extends Strategy with PredicateHelper ...

之上
  • 广播:如果联接的一侧估计的物理尺寸小于     用户可配置的[[SQLConf.AUTO_BROADCASTJOIN_THRESHOLD]]阈值     或者如果那一方有明确的广播提示(例如用户应用了     [[{{{}}]]函数到org.apache.spark.sql.functions.broadcast()),然后是那边     连接将被广播,另一方将被流式传输,没有改组    执行。如果加入的双方都有资格被广播,那么
  • 随机散列连接:如果单个分区的平均大小足以构建散列表。

  • 排序合并:如果匹配的连接键是可排序的。

答案 1 :(得分:2)

广播加入不支持完全外连接。它仅支持以下类型:

InnerLike | LeftOuter | LeftSemi | LeftAnti |存在加入| RightOuter

有关详细信息,请参阅JoinStrategy