外观
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 + 合理分区数常见坑(至少牢记这些)
阈值按「源文件大小」估
压缩 Parquet 10MB 解压后可能 200MB+,Broadcast 爆内存。维表不唯一
Broadcast Hash Join 放大行数,指标翻倍,像 Join 膨胀。广播整行宽表
只 join 3 个字段却 select * 维表,无谓撑大广播变量。Driver 收集大表当 Broadcast
旧写法collect自制 Map,Driver OOM;用官方 Broadcast Join。多表连续 Broadcast 叠加
单表可接受,三张维同时播,executor 堆叠加超限。忽略空分区 / 空维
空维广播「成功」但全 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}';