确认倾斜键需先查spark ui中input size与shuffle read size是否分布不均,再统计join键频次并检查默认键;加盐须同步打散两边、合理选n、仅对热点key操作,并重分区;优先验证小表真实体积与统计信息。

怎么确认是倾斜键在拖慢JOIN
别一上来就改SQL,先看Spark UI里那个卡住的Stage:如果Input Size分布严重不均(比如一个Task读几百GB,其他都是几GB),且Shuffle Read Size也跟着拉出长尾,基本就是倾斜键在作祟。日志里反复出现Disk spill或GC overhead limit exceeded也是强信号——这不是慢,是“不均”。
验证方法就两步:
- 对JOIN键做频次统计:
SELECT join_key, COUNT(*) FROM table GROUP BY join_key ORDER BY COUNT(*) DESC LIMIT 20 - 重点检查兜底值:
user_id = 0、product_id = -1、NULL这类常被事实表高频引用的“默认键”
大表JOIN小表时,加盐必须同步打散两边
加盐不是给大表随便加个随机数,而是让热点key在两张表中以完全一致的方式分裂。否则JOIN不上,结果为空或重复。
关键点:
- 盐值数量要合理:用
hash(join_key) % N代替RAND(),N通常取5–20;太小仍会撞上,太大徒增Shuffle量 - 只对确认的热点key加盐,比如
user_id IN (0, -999),其他key保持原样,避免无谓开销 - 小表必须同步扩容:用
LATERAL VIEW explode(array(0,1,...,N-1))把每条记录复制N份,每份带不同salt - 加盐后必须显式重分区,比如加
/*+ REPARTITION(200) */提示,否则Spark可能沿用原分区逻辑
比加盐更轻量的方案:先试试广播+统计信息更新
很多“倾斜”其实是假象——小表看着行数少,但STRING列多、序列化后膨胀到2GB以上,根本播不了。这时加盐是绕远路。
优先检查并操作:
- 用
DESCRIBE FORMATTED table_name查TotalSize字段,确认小表真实体积 - 执行
ANALYZE TABLE table_name COMPUTE STATISTICS更新统计信息,让CBO能识别小表并自动触发BroadcastHashJoin - 强制广播:加
/*+ BROADCAST(table_name) */hint,但前提是小表确实够小(建议 - 如果小表含大量重复STRING,考虑先用
STRING列做字典编码或转INT,再广播
两个大表JOIN时,局部加盐比全局加盐更可控
全局加盐(所有key都打散)会让原本均匀的key也参与Shuffle,反而放大开销。真正该动的只是那几个Top热点key。
推荐做法:
- 先用频次查询锁定前10–20个倾斜key,比如
SELECT user_id FROM ods_events GROUP BY user_id ORDER BY COUNT(*) DESC LIMIT 20 - 把大表拆成两部分:
WHERE user_id IN (hot_keys)走加盐路径,WHERE user_id NOT IN (hot_keys)走普通JOIN - 小表对应拆成两份:一份保留原key用于普通JOIN,一份按相同salt逻辑扩容用于加盐JOIN
- 最后用
UNION ALL合并结果,避免去重开销
这种分治策略下,95%的数据走原路径,只有热点部分承担加盐成本,整体Shuffle量和计算复杂度都更可控。最容易被忽略的是盐函数的一致性——哪怕只差一个括号,两边salt值对不上,JOIN结果就全空了。










