broadcast join适用于小表(≤默认10mb)与大表关联场景,通过广播小表至各executor避免shuffle;若小表超阈值或需强制优化,可显式调用broadcast()函数。

什么时候该用 BroadcastJoin 而不是 SortMergeJoin
Spark SQL 默认对大小表 JOIN 会自动选择 BroadcastHashJoin,但前提是小表不超过 spark.sql.autoBroadcastJoinThreshold(默认 10MB)。一旦小表实际体积超过阈值,就会退化为 SortMergeJoin,触发全量 Shuffle。
- 检查小表真实大小:用
df.count()+df.schema估算序列化后体积,别只看原始行数 - 手动强制广播:对确认是小维表的 DataFrame,显式调用
broadcast(df),再 JOIN,绕过阈值判断 - 注意反模式:把“逻辑上小”但未过滤的宽表直接广播——比如含大量 NULL 或冗余字段的用户维表,先
select必需列再广播
大表 JOIN 大表时如何避免全量 Shuffle
当两张表都很大,BroadcastJoin 不适用,又不想承受 SortMergeJoin 的排序+Shuffle 开销,唯一可行路径是让两表提前共分区(co-partitioned),触发窄依赖的 cogroup 级别 JOIN。
- 确保两表 key 字段类型一致且无隐式转换,否则
repartition后 hash 结果不一致,分区失效 - 用相同
Partitioner:比如都用HashPartitioner(200),且传给join(other, partitioner),不能只 repartition 而不显式传参 - 警惕上游算子破坏分区:
filter、map后分区信息丢失,必须在 JOIN 前重新repartition或coalesce
JOIN 前没过滤就 shuffle 是最大浪费
很多任务慢不是因为 JOIN 本身,而是 JOIN 输入数据量太大。Shuffle 数据量 ≈ 参与 JOIN 的每行记录序列化后的字节总和,和行数呈线性关系。
- 永远先
filter再join:比如订单表 JOIN 用户表前,先用where dt = '2026-06-13'缩小订单范围 - 避免 SELECT *:JOIN 前只
selectJOIN key 和后续需要的字段,减少单行序列化体积 - 对大表做预聚合:如需统计每个用户的订单数,先在订单表上
groupBy("user_id").count(),再与用户表 JOIN,而非拉全量订单记录
Spark SQL 中 JOIN 字段没索引?那不是问题
Spark 是计算引擎,不依赖底层存储的 B+ 树索引。所谓“JOIN 字段建索引”在 Hive/MySQL 里有效,在 Spark SQL 里无效——它根本不会读索引文件。
- 真正起作用的是数据分布:key 的哈希均匀性决定 reducer 负载是否倾斜,而不是有没有索引
- 遇到倾斜时,别想着加索引,该拆分异常 key(如用
concat(key, rand()))、该加盐(salting)就加盐 - 如果数据源是 Hive 表且走 Tez/MR 引擎,索引才生效;但 Spark on Hive 仍走 Spark 自己的执行计划,忽略 Hive 索引
repartition(200) 得到的 RDD 看似分区数一样,但分区器对象不同,JOIN 时仍会 shuffle。必须确保它们共享同一个 Partitioner 实例,否则所有优化都白搭。











