GROUP BY 倾斜主因是 key 分布不均导致 shuffle 失衡,而非 ORDER BY;可通过抽样 SQL、日志或 Web UI 快速定位热点 key;NULL 或低基数字段需单独处理或加盐(如 RAND(seed) 控制打散),盐值选 10–50 并仅作用于确认热点,两阶段聚合须确保二次聚合轻量。
GROUP BY 本身不产生排序,所谓“GROUP BY 排序后倾斜”其实是误读——真正拖慢的不是 ORDER BY,而是 GROUP BY 阶段因 key 分布不均导致 shuffle 严重失衡。排序(ORDER BY)发生在聚合之后,它只影响最终输出顺序,不改变分组过程的负载分布。
别等任务跑完再查,先用轻量 SQL 抽样看分布:
SELECT key, COUNT(*) AS cnt FROM table GROUP BY key ORDER BY cnt DESC LIMIT 10,重点看第一行 cnt 是否超过第二行的 10 倍——这是强倾斜信号LIMIT 100000 可能漏掉长尾热点,尤其当数据按时间写入、新数据集中在末尾时,建议用 TABLESAMPLE(1) 或按分区抽样hive.groupby.skewindata=true,运行后检查日志是否有 skew join detected 或类似提示;Spark SQL 则必须结合 Web UI 查 shuffle write 最大的 stage,再反推对应 SQL 片段NULL 被所有引擎归为同一组,若百万行 user_id 为 NULL,就全压到一个 reducer 上。
COALESCE(user_id, FLOOR(RAND() * 10000)) 把 NULL 映射成随机整数,但注意 RAND() 在 Spark 中需带 seed(如 RAND(123))才可复现SELECT 'NULL' AS key, COUNT(*) FROM t WHERE user_id IS NULL UNION ALL SELECT user_id AS key, COUNT(*) FROM t WHERE user_id IS NOT NULL GROUP BY user_id
user_id BIGINT NOT NULL DEFAULT -1,用 -1 占位并加注释,从源头规避加盐不是打乱 key,而是可控打散:要保证同一原始 key 的所有记录被均匀分配到多个子 key,且后续能准确还原。
CONCAT(key, '_', RAND()) —— 同一行内多次调用 RAND() 结果不同,导致同一 key 被拆到多个子组,无法正确汇总CASE WHEN key = 'hot_key' THEN CONCAT('salt_', FLOOR(RAND() * 20), '_', key) ELSE key END,RAND() 在同一行内值稳定RAND(123) 指定 seed,否则每次执行盐值不同,结果不可复现加盐只是手段,核心是把“单点重负载”变成“多点轻负载+轻量合并”。比如统计各 region 的订单总额:
SELECT region, SUM(sum_amt) AS total_amtFROM ( SELECT region, SUM(amount) AS sum_amt, CAST(RAND(42) * 32 AS INT) AS salt FROM sales_data GROUP BY region, salt) tGROUP BY region;
RAND(42) 确保 salt 可复现,32 是经验性盐值粒度,非固定值COUNT(DISTINCT) 不能直接套用该模式,它本身会掩盖 key 分布,定位倾斜时务必换回 COUNT(*) + GROUP BY
加盐的边界很窄:盐值要够大以分散负载,又不能大到让 shuffle 数据量翻倍;只打散确认热点,不碰正常 key;还原阶段的二次聚合必须轻量——这些细节稍一松懈,优化就变成负优化。