广播连接省掉了什么
大表关联小表时,默认做法是把两张表按 key 重分布再连接,网络开销很大。广播连接改为把小的那张表整份复制到每个执行节点,大表原地不动,省掉一次大规模 shuffle。Spark 会按阈值自动判断,也可以用 broadcast 提示强制指定。
基本信息
| 机制 | 小表复制到各 executor,避免 shuffle |
|---|---|
| 自动阈值 | spark.sql.autoBroadcastJoinThreshold,默认 10MB |
| 手动指定 | broadcast(小表) 提示或 broadcast hint |
| 适用 | 维表、字典表、过滤用小结果集 |
| 代价 | 占用每个 executor 的堆内存 |
典型使用场景
- 事实表关联维表:订单明细左连门店、商品维表
- 大表过滤:先算出小范围白名单再广播后过滤,减少参与连接的数据量
- 维表映射分发:把码表作为广播变量下发到各任务,避免反复查库
- 迭代计算:同一维表在循环里被反复使用,广播一次全程复用
- 消除数据倾斜:小表极小时,广播能绕开倾斜 key 造成的长尾
广播连接示例
广播连接示例
from pyspark.sql.functions import broadcast
orders = spark.table('dwd_trade_order_di').where("dt='2026-10-12'")
store = spark.table('dim_store').where('is_current=1')
# 显式广播维表:适合维表远小于阈值上限的场景
res = orders.join(broadcast(store), 'store_id', 'left')
res.write.mode('overwrite').insertInto('dws_trade_store_day')
# 也可调低自动阈值,避免优化器误判
spark.conf.set('spark.sql.autoBroadcastJoinThreshold', 10 * 1024 * 1024)连接策略对照
| 策略 | 触发条件 | 代价 |
|---|---|---|
| 广播连接 | 一侧远小于阈值 | 每节点内存占用 |
| shuffle 连接 | 两侧都大 | 网络传输与磁盘落盘 |
| 分桶对齐连接 | 两表桶数与键一致 | 建表结构约束 |
| 倾斜加盐 | 存在超大 key | 额外计算与代码复杂度 |
维表实际大小往往比想象的大:字符串字段多、历史版本没过滤,很容易从几十兆涨到几百兆,自动广播阈值一旦调高就会误判。
强行广播超过内存容量的表,会出现执行节点内存溢出或频繁垃圾回收,任务表现为长时间卡在某个阶段而不报错。
广播表与广播变量的区别
广播连接解决的是 join 阶段避免 shuffle,作用对象是一张表;广播变量是把一份只读数据分发到各执行节点,作用对象可以是字典、数组、映射这类非表结构。前者写在 join 表达式里,后者需要显式创建并传递,混用会让代码意图变得难以理解。
调优检查顺序
- 先确认维表是否已过滤历史版本,把数据量压到最小
- 再看 EXPLAIN 里实际选用的连接策略,判断是否发生广播
- 维表接近阈值时报错时,评估改写为分桶或改用过滤器
- 广播后出现内存告警,优先检查维表真实大小而不是加内存
- 把结论写进任务注释,避免下一个人重复试错