Skip to content

Broadcast Join 适用边界 ​

难度与前置

难度:进阶
前置:写过 Spark SQL Join;知道 Broadcast 能免大表 Shuffle。
读完能做什么:判断何时 Broadcast、何时禁止,配置阈值与提示,处理溢出与倾斜,并用 Stage 指标验收。

适用场景 ​

  • 大事实表 Join 小维表(维度 MB~数百 MB 级,视 executor 内存)
  • 想消除维表侧 Shuffle,压 Spill 与耗时
  • AQE 可自动转 Broadcast,需理解边界以免「以为会播、实际在 Shuffle」

不适用:

  • 两侧都大(GB+),强行 Broadcast → Driver/Executor OOM
  • 维表「看起来小」但有极端倾斜热键,Broadcast 后仍爆(见 倾斜 Join)
  • 需要排序合并保证的特殊语义且禁用自动优化的场景(少见)

真实场景(一条) ​

订单事实 2TB Join 城市维 80MB。未开 Broadcast,走 SortMerge,Shuffle 写 1.2TB,耗时 45min。加 broadcast 提示并确认 autoBroadcastJoinThreshold,维表广播成功,作业 8min。后一次维表误扩到 3GB(含历史),Broadcast 失败改 SMJ 且 Driver 收集超时——加行数/大小门禁与回退策略后恢复稳定。

怎么做(步骤清单) ​

1. 先估大小,再谈 Join 策略 ​

text
广播侧估算 ≈ 行数 × 平均行宽 × 膨胀系数(编码/对象头)
经验:单侧 << executor 可用内存,且 << spark.sql.autoBroadcastJoinThreshold
默认阈值常见 10MB(版本/发行版可能不同)——生产按集群调大,但有上限纪律
Join 侧建议
维表 < 阈值自动或 hint Broadcast
维表略超但稳定显式 /*+ BROADCAST(d) */ 并观察内存
维表 > 数百 MB~GB禁止 Broadcast,走 SMJ/SHJ + AQE
两侧都大分区裁剪、桶表、倾斜专项

2. 配置与提示 ​

sql
-- 会话
SET spark.sql.autoBroadcastJoinThreshold = 64m;
SET spark.sql.adaptive.enabled = true;
SET spark.sql.adaptive.autoBroadcastJoinThreshold = 64m; -- AQE 侧(版本相关)

-- 提示(小表别名)
SELECT /*+ BROADCAST(c) */
  o.order_id, o.amt, c.city_name
FROM fact_order_di o
JOIN dim_city_df c
  ON o.city_id = c.city_id
 AND c.dt = '${biz_date}'
WHERE o.dt = '${biz_date}';
scala
import org.apache.spark.sql.functions.broadcast
val out = fact.join(broadcast(dim), Seq("city_id"), "left")

3. 计划确认(必须做) ​

sql
EXPLAIN FORMATTED
SELECT /*+ BROADCAST(c) */ ...;
-- 期望:BroadcastHashJoin,BuildRight/BuildLeft 为维表

4. 空值与重复键 ​

sql
-- 广播前先保证维表键唯一,否则放大事实行
SELECT city_id, COUNT(*) AS cnt
FROM dim_city_df
WHERE dt='${biz_date}'
GROUP BY city_id
HAVING COUNT(*) > 1;
-- 期望 0 行

-- NULL 键:Broadcast 后仍不匹配,LEFT 保留 NULL;勿误当丢数

5. 与 AQE / 动态优化协作 ​

  • AQE 可能把 SMJ 运行时改为 Broadcast——小表统计要准(ANALYZE / 动态分区统计)
  • 统计过期 → 误判阈值 → 错误策略;补数后重收集统计
  • 大表侧严重倾斜:Broadcast 只解 Shuffle 形态,不解热键单点(仍要盐析等)

6. 失败回退 ​

text
若 Broadcast 超时 / OOM:
1. 去掉 BROADCAST hint
2. 下调 autoBroadcastJoinThreshold
3. 维表先过滤/投影必要列再 Join
4. 改 SMJ + 合理分区数

常见坑(至少牢记这些) ​

  1. 阈值按「源文件大小」估
    压缩 Parquet 10MB 解压后可能 200MB+,Broadcast 爆内存。

  2. 维表不唯一
    Broadcast Hash Join 放大行数,指标翻倍,像 Join 膨胀。

  3. 广播整行宽表
    只 join 3 个字段却 select * 维表,无谓撑大广播变量。

  4. Driver 收集大表当 Broadcast
    旧写法 collect 自制 Map,Driver OOM;用官方 Broadcast Join。

  5. 多表连续 Broadcast 叠加
    单表可接受,三张维同时播,executor 堆叠加超限。

  6. 忽略空分区 / 空维
    空维广播「成功」但全 NULL,业务当 Join 配错。

如何验收(可量化) ​

  • [ ] EXPLAIN 显示 BroadcastHashJoin,Build 侧为预期小表
  • [ ] Spark UI:无大表侧无意义的超大 Shuffle Write(对比优化前)
  • [ ] Executor 峰值内存 / GC 可接受,无 Broadcast 超时
  • [ ] 维表键唯一校验通过;输出行数符合 Join 语义
  • [ ] 指标抽样与非 Broadcast 路径(小样本)一致
sql
-- 行数与金额一致性(相对 SMJ 小样本)
SELECT COUNT(*) AS cnt, SUM(amt) AS s
FROM (
  SELECT /*+ BROADCAST(c) */ o.order_id, o.amt
  FROM fact_order_di o JOIN dim_city_df c ON o.city_id=c.city_id
  WHERE o.dt='${biz_date}' AND c.dt='${biz_date}' AND o.city_id < 100
) t;
bash
# UI / 指标验收清单(手工或 API)
echo "check: BroadcastExchange size MB < threshold"
echo "check: ShuffleWrite of dim side ~ 0"
echo "check: stage duration regression < baseline"
text
Spark UI → SQL → 节点:
BroadcastHashJoin
  +- BroadcastExchange
  +- FileScan fact ...

相关面试题 ​

占位:与题库小程序对齐后补链接(建议关联:Broadcast Join、AQE、Join 策略选择、OOM)。


改写说明:结构参考 Wood 原创 Spark 调优实践,已改写成可执行清单;未包含未公开集群参数原值。

附:阈值决策速查 ​

广播表估算大小建议动作
< 10MB默认可 Broadcast
10~64MB显式 hint + 观察 UI
64~256MB仅在大内存池、投影后列很少时尝试
> 256MB默认禁止;走 SMJ/分桶/预过滤
sql
-- 投影后再广播,缩小 Build 侧
SELECT /*+ BROADCAST(c) */ o.*, c.city_name
FROM fact_order_di o
JOIN (
  SELECT city_id, city_name FROM dim_city_df WHERE dt='${biz_date}'
) c ON o.city_id = c.city_id
WHERE o.dt='${biz_date}';