SparkSQL 广播超时排查:小表但是多分区 = BroadcastTimeout

背景

日常跑批中一个 SparkSQL 任务突然报错失败,该任务之前一直稳定运行。任务逻辑比较见到那,并且涉及的数据量和 SQL 逻辑近期没有变动。

现象

任务执行约 7 分钟后抛出异常,关键报错信息如下:

vbscript 复制代码
26/05/06 14:52:25 ERROR BroadcastExchangeExec: Could not execute broadcast in 300 secs.

Spark 在执行 BroadcastHashJoin 时,等待广播变量超过了默认的 300 秒超时阈值。

排查过程

第一步:确认广播超时的对象

看到 BroadcastExchangeExec 超时,第一反应是:哪张表被广播了?为什么超时?

Spark 在执行 left JOIN 时,如果一侧的表大小低于 spark.sql.autoBroadcastJoinThreshold(默认 10MB),会自动选择 BroadcastHashJoin,将小表广播到所有 Executor。

按理说小表广播应该很快(并且确认了left join的都是产品级别的维表),300 秒超时不太正常。

第二步:日志中寻找线索

在 ERROR 级别的日志中只能看到超时的结论,没有直接给出是哪张表导致的

这里走了一点弯路------一开始只关注了 ERROR 和 WARN 级别的日志,没办法继续往下定位

后来定位到失败task的INFO 级别日志, 确认108这个task从开始到结束大概5min, 是executor自行kill了

yaml 复制代码
*26/05/06 14:47:19* INFO Executor: Running task 108.0 in stage 5.0 (TID 578)
26/05/06 14:47:19 INFO ShuffleBlockFetcherIterator: Getting 1 (97.0 B) non-empty blocks including 1 (97.0 B) local and 0 (0.0 B) host-local and 0 (0.0 B) remote blocks
26/05/06 14:47:19 INFO ShuffleBlockFetcherIterator: Started 0 remote fetches in 0 ms
.......
26/05/06 14:52:25 INFO Executor: Running task 108.0 in stage 9.0 (TID 2516)
26/05/06 14:52:25 INFO ShuffleBlockFetcherIterator: Getting 1709 (380.0 KiB) non-empty blocks including 388 (85.7 KiB) local and 0 (0.0 B) host-local and 1321 (294.3 KiB) remote blocks
26/05/06 14:52:25 INFO ShuffleBlockFetcherIterator: Started 5 remote fetches in 5 ms
*26/05/06 14:52:25* INFO *Executor: Executor is trying to kill task 108.0* in stage 9.0 (TID 2516), reason: Stage cancelled
26/05/06 14:52:25 INFO Executor: Executor interrupted and killed task 108.0 in stage 9.0 (TID 2516), reason: Stage cancelled

再根据失败job的DAG定位到源头的表, 这个表的分区因业务需求扫描了一千多个分区

第三步:定位根因

问题明确了:

被广播的 Hive 表数据量小(满足广播阈值),但分区数极多(1000+)。

Spark 在广播前需要做以下工作:

  1. 列举所有分区目录(FileStatusCache / InMemoryFileIndex)
  2. 读取每个分区下的文件元数据
  3. 合并数据并序列化为广播变量分发到各 Executor

当分区数达到千级别时,步骤 1 和 2 的 文件列举开销会非常大,尤其是在 HDFS 上需要大量 RPC 调用。这个阶段是在 Driver 端串行执行的,最终导致整体广播时间超过了 300 秒的限制。

核心矛盾 :Spark 的自动广播判断只看数据量大小 ,不考虑分区数量。一张数据量 < 10MB 但有 1000+ 分区的表,会被判定为"小表"触发广播,但实际广播成本远超预期。

解决方案

短期:调大广播超时阈值

properties 复制代码
spark.sql.broadcastTimeout=1200

将超时时间从默认 300 秒调大到 600 秒,确保有足够时间完成分区文件的列举和广播。

长期建议(可选)

方案 做法 适用场景
禁用该表的广播 SQL 中使用 /*+ SHUFFLE_HASH(t) *//*+ SHUFFLE_MERGE(t) */ 强制走 SortMergeJoin 明确知道某张表不适合广播
降低广播阈值 spark.sql.autoBroadcastJoinThreshold=-1 或设更小值 全局禁用/收紧自动广播
合并小分区 对该表做分区治理,合并过多的细碎分区 从源头解决问题

总结

项目 内容
现象 BroadcastExchangeExec 300 秒超时
根因 小表分区数过多(1000+),分区文件列举耗时超限
关键排查手段 查看 INFO 级别日志,发现大量分区扫描记录
修复 调大 spark.sql.broadcastTimeout
教训 自动广播只看数据量不看分区数,多分区小表是隐藏的坑

延伸思考

"小表"不等于"广播成本低"。在评估 BroadcastJoin 是否合适时,除了数据量,还需要关注:

  • 分区数量(影响文件列举速度)
  • 小文件数量(影响 I/O 次数)

遇到类似场景,值得在数仓治理层面定期巡检分区数异常的表,从源头避免此类问题。

相关推荐
大大大大晴天5 小时前
从 HDFS 到对象存储:计算存储分离如何重塑云原生大数据底座
大数据
用户36105886261213 小时前
SparkStreaming 之 updateStateByKey 算子详解及代码实现
大数据·spark
咖啡屋和酒吧15 小时前
“隐性肩颈紧张”正在透支精力|没有酸痛,不代表肩颈处于健康状态
大数据·精选
港股研究社16 小时前
专注履约底座,顺丰同城在即时零售效率时代提升增长动能
大数据·人工智能
AI_yangxi18 小时前
短视频矩阵系统选哪家
大数据·人工智能·矩阵
lupai1 天前
手机在网状态接口实测效果与质量评估
大数据·python·智能手机·api接口
大模型丫丫1 天前
Hermes Agent:轻量级智能体框架实战指南
大数据·运维·服务器
大大大大晴天1 天前
每天认识一个组件:SQL网关Apache Kyuubi
大数据
2601_962218471 天前
万象生鲜系统冷链物联网接入技术实现生鲜企业温控管理数字化
大数据·运维·微服务·云原生·架构
2601_962074811 天前
大数据-264 实时数仓 - Canal MySQL的binlog研究 存储目录 变动信息 配置MySQL
大数据·数据库·mysql