当小表能完整塞入每个worker内存且行数稳定在几万以内时,应选broadcast;否则必须用partitioned。判断依据是explain(type distributed)中exchangenode类型:broadcast表示广播,repartition表示分区。

什么时候该用 BROADCAST 而不是 PARTITIONED join?
当小表(比如维度表)能完整塞进每个 worker 的内存里,且行数稳定在几万以内时,BROADCAST 是更优选择。它把小表复制到所有节点,大表分片后本地 join,避免 shuffle 开销。
- 常见错误现象:
Query exceeded per-node user memory limit—— 小表其实没那么小,或被误判为“可广播”,导致内存溢出 - 判断依据不是“表名带 dim_”或“看起来不长”,而是看
EXPLAIN (TYPE DISTRIBUTED)输出里小表是否真走BROADCAST分发;若实际走了PARTITIONED,说明 Trino 自动降级了 - Trino 默认有
join-distribution-type=automatic,但自动判断依赖统计信息;没 ANALYZE 过的表大概率被当成大表处理 - 手动强制用
BROADCAST:在 join 前加/*+ BROADCAST(t) */(t是别名),但必须确保t真的小——否则 worker 直接 OOM
PARTITIONED join 在什么场景下不可替代?
当两个表都大,或者小表实际有几十 GB(比如宽口径用户标签表)、无法广播时,PARTITIONED 是唯一可行路径。它靠 hash 分区 + shuffle 对齐数据,代价是网络和序列化开销明显上升。
- 使用场景:事实表之间关联(如订单 + 支付流水)、未做分区裁剪的日期范围 join、join key 高基数且分布倾斜不严重
- 性能影响:如果 join key 有严重数据倾斜(比如
tenant_id = 'default'占 40% 行数),PARTITIONED会导致个别 task 拖慢整条 pipeline - 兼容性注意:某些 connector(如 Delta Lake on S3)对
PARTITIONED的 shuffle 效率偏低,比 Hive connector 多 20–30% 执行时间 - 可配合
JOIN REORDER或显式/*+ JOIN_ORDERING */控制驱动表顺序,让更易过滤的大表先走 filter
如何确认当前 query 实际走的是哪种 join?
不能只信 SQL 写法或 hint,得看执行计划里真实的数据分发行为。
- 运行
EXPLAIN (TYPE DISTRIBUTED) SELECT ...,找ExchangeNode类型:出现BROADCAST表示小表被广播;出现REPARTITION则是PARTITIONED - 注意
TableScanNode下方紧邻的 exchange 类型——有些 case 表面写了/*+ BROADCAST(t) */,但优化器发现t统计信息缺失,仍 fallback 到REPARTITION - 生产环境建议定期跑
ANALYZE table_name,尤其对高频 join 的小表;否则estimatedRowCount为UNKNOWN,优化器不敢广播 - 查看
system.runtime.tasks中各 stage 的peakUserMemoryBytes:若 broadcast side 的 task 内存突增 5x 以上,说明广播表体积已逼近临界点
容易被忽略的配置与边界条件
很多问题不是逻辑写错,而是默认值或 connector 行为和直觉不一致。
-
task.max-worker-threads影响 broadcast join 并行度:太小会让广播加载变慢,太大可能挤占其他查询资源 - 某些 connector(如 PostgreSQL)不支持 broadcast join,即使 hint 也静默忽略——查
EXPLAIN是唯一验证方式 - Trino 400+ 版本引入
join_distribution_type会受optimizer.join-reordering-strategy干扰;设成ELIMINATE_CROSS_JOINS可能意外禁用 broadcast - 如果小表本身是
UNION ALL多个子查询拼出来的,Trino 当前版本(≤438)不会自动识别其总大小,需手工合并或物化为临时表再 hint
真正卡住人的往往不是“该选哪个”,而是小表到底有多大、统计信息准不准、connector 支不支持、hint 被没被吃掉——这些细节不验一遍,调参和 hint 都是空谈。










