Spark_SparkSQL_broadcast join不生效问题

问题与排查过程

大数据计算通常会存在大表join小表的情况,如果相对较小的表允许广播到各个executor的话,可以使用广播方式mapjoin,这样还可以避免数据倾斜。

平时看文档记着有个参数是:

spark.sql.autoBroadcastJoinThreshold10485760 (10 MB)Configures the maximum size in bytes for a table that will be broadcast to all worker nodes when performing a join. By setting this value to -1 broadcasting can be disabled. Note that currently statistics are only supported for Hive Metastore tables where the command ANALYZE TABLE <tableName> COMPUTE STATISTICS noscan has been run.

看到auto以为spark执行join时候会根据表的大小自动切换广播join;今天跑任务时候发现虽然满足这个阈值却无法进行广播join,只好求助于官方文档,最后发现描述是:

可以配置如上属性的阈值,指定一个进行广播join的小表大小临界值,当数值设置为-1时候禁止使用广播join,最后重点:表大小的统计信息目前只支持Hive Metastore tables,言外之意只有表可以借助 ANALYZE TABLE <tableName> COMPUTE STATISTICS noscan命令可以获取大小的hive表才可以进行广播join,因此如果你的小表是一个DataFrame计算而来的小表进行join的时候也就不会进行自动优化为广播join了。

查看Hive分区表的统计分析:

ANALYZE TABLE app_user_order(dt='2019-05-01')  COMPUTE STATISTICS noscan;

输出:

Partition app.app_dm_online_logdt=2019-05-01 stats: [numFiles=1, numRows=178, totalSize=308285, rawDataSize=308107]

因此可以推断Spark进行优化广播join时候获取小表信息是根据元数据信息获取的,看源码可以查到相关证据。根据如下三个方法调用就可以看到计算大小的逻辑:

org.apache.spark.sql.catalyst.plans.logical.statsEstimation.LogicalPlanStats#stats

org.apache.spark.sql.catalyst.plans.logical.statsEstimation.SizeInBytesOnlyStatsPlanVisitor#default

org.apache.spark.sql.catalyst.plans.logical.statsEstimation.EstimationUtils#getOutputSize:

def getOutputSize(
      attributes: Seq[Attribute],
      outputRowCount: BigInt,
      attrStats: AttributeMap[ColumnStat] = AttributeMap(Nil)): BigInt = 
    // We assign a generic overhead for a Row object, the actual overhead is different for different
    // Row format.
    val sizePerRow = 8 + attributes.map  attr =>
      if (attrStats.contains(attr)) 
        attr.dataType match 
          case StringType =>
            // UTF8String: base + offset + numBytes
            attrStats(attr).avgLen + 8 + 4
          case _ =>
            attrStats(attr).avgLen
        
       else 
        attr.dataType.defaultSize
      
    .sum

    // Output size can't be zero, or sizeInBytes of BinaryNode will also be zero
    // (simple computation of statistics returns product of children).
    if (outputRowCount > 0) outputRowCount * sizePerRow else 1
  

如上代码便是评估Hive数据表的大小。

解决方法 

那对于经过transform而来的小表是不是就不能进行广播join了呢?答案是可以的,可以使用org.apache.spark.sql.functions#broadcast进行对小表进行强制广播join,但是需要表大小合适进行广播join并且保证Driver以及Executor内存足够。

最后回头了解一下Hive command:

ANALYZE TABLE db.tableName(dt='2019-05-01')  COMPUTE STATISTICS noscan;
  • 这个命令很有用,大概简单描述可以做如下事情:查看Hive表或者分区信息,例如:行数,分区数,文件数,以及数据大小(byte);也可以使用describle extended tablename进行查看统计信息。
  • hive统计信息何时被计算?通常,当你创建表之后Hive会自动计算统计数据信息并存储到metastore,可以在创建表时候显示指定:
hive.stats.autogather=false

这样表的统计信息将不会被自动计算。

除此之外还需要注意的是:df.join(broadcast(small_df),Seq("join_key"),"join type")  需要将broadcast(small_df)放在join的右边,否则不会执行广播join。

参考:

Performance Tuning - Spark 3.4.1 Documentation

Spark SQL中的broadcast join分析_dabokele的博客-优快云博客

有关Hive Analyze的命令参考:

Column Statistics in Hive - Apache Hive - Apache Software Foundation

Hive ANALYZE TABLE Command - Table Statistics - DWgeek.com

以上是关于Spark SQL有关broadcast join的不生效问题的主要内容,如果未能解决你的问题,请参考以下文章

### Spark SQL 中 Broadcast Join使用方法及 SQL 实例 #### 什么是 Broadcast JoinBroadcast Join 是一种优化策略,在 Spark SQL 中用于处理小表和大表之间的连接操作。其核心思想是将较小的表广播到集群的所有节点上,使得每个节点都可以在本地完成数据的连接操作,而无需通过 shuffle 来移动大量数据[^4]。 当满足以下条件之一时,Spark 将会尝试应用 Broadcast Join: 1. 显式指定 `BROADCAST` 提示(hint),强制将某个表作为广播对象。 2. 自动广播机制生效,即如果某张表的大小小于 `spark.sql.autoBroadcastJoinThreshold` 配置值,则自动触发广播行为[^4]。 --- #### 如何启用 Broadcast Join? 可以通过设置参数来控制 Broadcast Join 的行为: ```sql SET spark.sql.autoBroadcastJoinThreshold=10m; ``` 默认情况下,`autoBroadcastJoinThreshold` 设置为 10 MB(单位可以是 KB/MB)。如果将其设为 `-1`,则禁用自动广播功能[^5]。 --- #### Broadcast Join 的语法与实例 以下是几种常见的 Broadcast Join 使用方式及其对应的 SQL 示例: ##### 方法一:显式指定 BROADCAST Hint 可以在查询中手动添加 `/*+ BROADCAST(table_name) */` 提示,告诉 Spark 对特定的小表进行广播。 **SQL 示例:** ```sql SELECT /*+ BROADCAST(small_table) */ big_table.* FROM big_table JOIN small_table ON big_table.id = small_table.id; ``` 此语句表示将 `small_table` 广播至所有执行节点,并与 `big_table` 进行本地连接[^1]。 --- ##### 方法二:隐式依赖 autoBroadcastJoinThreshold 参数 如果不希望手动干预,也可以依靠 Spark 的自动判断机制。只需确保目标表的大小低于阈值即可。 **SQL 示例:** ```sql -- 假设 small_table 大于 10M,但小于当前 threshold (e.g., 1G) SET spark.sql.autoBroadcastJoinThreshold=1g; SELECT * FROM big_table JOIN small_table ON big_table.id = small_table.id; ``` 在这种场景下,即使未提供任何提示,Spark 可能仍然会选择 Broadcast Join 方案[^3]。 --- ##### 方法三:结合复杂查询场景 对于更复杂的查询结构,同样支持嵌套子查询或者多层 JOIN 场景下的广播优化。 **SQL 示例:** ```sql SELECT /*+ BROADCAST(subquery_result) */ main_table.* FROM ( SELECT id, value FROM intermediate_results WHERE condition = true ) AS subquery_result JOIN main_table ON main_table.id = subquery_result.id; ``` 这里展示了如何对中间结果集(subquery)实施广播优化[^2]。 --- #### 性能调优建议 为了充分发挥 Broadcast Join 的优势,请注意以下几点: 1. **合理调整广播阈值**:根据实际业务需求修改 `spark.sql.autoBroadcastJoinThreshold` 参数,避免因过低或过高而导致性能下降[^5]。 2. **监控运行计划**:利用 `EXPLAIN` 查看物理执行计划,确认是否成功启用了 Broadcast Hash Join[^4]。 ```sql EXPLAIN EXTENDED SELECT /*+ BROADCAST(small_table) */ big_table.* FROM big_table JOIN small_table ON big_table.id = small_table.id; ``` 3. **评估数据分布**:确保被广播的表确实足够小;否则可能引发内存溢出等问题[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值