Spark SQL 中 Broadcast Join 一定比 Shuffle Join 快?那你就錯了。

本資料來自 Workday 的軟件開發工程師 Jianneng Li 在 Spark Summit North America 2020 的 《On Improving Broadcast Joins in Spark SQL》議題的分享。相關 PPT 可以到 你要的 Spark AI Summit 2020 PPT 我已經給你整理好了 裏面獲取。

背景

相信使用 Apache Spark 進行數據分析的同學對 Spark 中的 Broadcast Join 比較熟悉,其在 Join 之前會把一端比較小的表廣播到參與 Join 的 worker 端,具體如下:

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop

相比 Shuffle Join,Broadcast Join 的優勢主要有:

避免把大表的數據 shuffle 到其他節點;很自然地處理數據傾斜

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop 

很多人得出結論:在 Broadcast Join 適用的情況下,Broadcast Join 是要比 Shuffle Join 快!但事實是這樣的嗎?

TPC-H 測試

在得出結論之前我們先來進行 TPC-H 測試,來看下是不是 Broadcast Join 一定要比 Shuffle Join 快。測試條件如下:

數據集 10GB;查詢:6千萬條數據的 lineitem 表 join 1.5千萬的 orders 表Driver 的配置:1 core, 12 GBExecutor 的配置:一個 instance,18 cores, 102 GB

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop

從上面可以結果可以看出,Broadcast Join 比 Shuffle Join 跑的慢!

Broadcast Join 機制

在理解上面結果之前,我們先來看下 Broadcast Join 的運行機制。

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop

在進行 Broadcast Join 之前,Spark 需要把處於 Executor 端的數據先發送到 Driver 端,然後 Driver 端再把數據廣播到 Executor 端。如果我們需要廣播的數據比較多,比如我們把 spark.sql.autoBroadcastJoinThreshold 這個參數設置到 1G,但是我們的 Driver 端的內存值設置爲 500M,那這種情況下會導致 Driver 端出現 OOM。根據前面的分析,上面 TPC-H 結果慢是因爲:

Driver 端需要 collects 1.5千萬條的數據;Driver 端構建 hashtable;Driver 把構建好的 hashtable 發送到 Executor 端;Executor deserializes hashtable。

所以說由於當前 Broadcast Join 的運行機制,這就導致即使在 Broadcast Join 適用的情況下,Broadcast Join 不一定比 Shuffle Join 快。

過往記憶大數據提示,大家如果對這部分代碼感興趣可以參看 BroadcastExchangeExec.scala 類的相關代碼,其先調用 org.apache.spark.sql.execution.SparkPlan 類裏面的 executeCollectIterator 方法,其主要是將數據從 Executor 發送到 Driver,大家可以看到裏面調用了 getByteArrayRdd().collect():

private[spark] def executeCollectIterator(): (Long, Iterator[InternalRow]) = {
    val countsAndBytes = getByteArrayRdd().collect()
    val total = countsAndBytes.map(_._1).sum
    val rows = countsAndBytes.iterator.flatMap(countAndBytes => decodeUnsafeRows(countAndBytes._2))
    (total, rows)
}

然後到 relationFuture 變量初始化:

private1 lazy val relationFuture: Future[broadcast.Broadcast[Any]] = {
    SQLExecution.withThreadLocalCaptured[broadcast.Broadcast[Any]](
      sqlContext.sparkSession, BroadcastExchangeExec.executionContext) {
          try {
            // 這個地方就是前面說的將數據 Collect 到 Driver 端:
            val (numRows, input) = child.executeCollectIterator()
            // 這裏省去了一部分代碼


            // Construct the relation.
            val relation = mode.transform(input, Some(numRows))


            // 這裏省去了一部分代碼


            val beforeBroadcast = System.nanoTime()
            longMetric("buildTime") += NANOSECONDS.toMillis(beforeBroadcast - beforeBuild)


            // Broadcast the relation
            // 這個地方就是前面說的需要先 broadcast 數據到 Executor 端
            val broadcasted = sparkContext.broadcast(relation)
            longMetric("broadcastTime") += NANOSECONDS.toMillis(
              System.nanoTime() - beforeBroadcast)
            val executionId = sparkContext.getLocalProperty(SQLExecution.EXECUTION_ID_KEY)
            SQLMetrics.postDriverMetricUpdates(sparkContext, executionId, metrics.values.toSeq)
            promise.trySuccess(broadcasted)
            broadcasted
          } catch {


            // 這裏省去了一部分代碼   
          }
    }
}

提升 Broadcast Join 的性能

針對上面的分析,我們能不能不把數據 collect 到 Driver 端,而直接在 Executor 端之間進行數據交換呢?這就是 Workday 的工程師團隊給我們帶來的 Executor 端的 broadcast,這項工作可以參見 SPARK-17556。我們來看看 Executor 端的 broadcast 工作原理:

Executors 把 Join 需要的數據 broadcasted 給其他 Executors;Driver 端只負責記錄 Executors 端的 block 信息,這樣其他 Executor 就可以知道 block 可以從哪些 Executor 獲取。

具體流程如下:

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop

測試結果

Workday 的工程師分別測試了以下三種測試場景:

數據量不變,分別測試不同 core 的性能;lineitem 表大小不同測性能;加大 orders 表的大小

結果如下:

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop

如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop 總結起來就是:

在數據量一樣的情況下,如果 core 的個數比較多,Shuffle Join 是有優勢的;如果非廣播的表數據量數據量越來越大,Broadcast Join 是有優勢的;如果加大廣播表的數據量,Driver 端的 Broadcast 是跑不出結果,Executor 的 Broadcast Join 是比較快的。

根據上面的結論,所以大家要知道 Broadcast 不一定比 Shuffle 快。另外,Executor 端的 Broadcast 特性是2016年9月就提的,截止到最新的 Apache Spark 3.0.0 這個功能還沒有合併到主分支,如果大家有需要這個,可以自行合併。

猜你喜歡

1、Spark 背後的商業公司收購的 Redash 是個啥?

2、馬鐵大神的 Apache Spark 十年回顧

3、YARN 在字節跳動的優化與實踐

4、Apache Spark 3.0.0 正式版終於發佈了,重要特性全面解析

過往記憶大數據微信羣,請添加微信:fangzhen0219,備註【進羣】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章