数据倾斜是什么
数据倾斜指某个 key 的数据量远大于其他 key,导致处理该 key 的单个 reduce 任务长时间运行:其他任务早早结束,整个作业却卡在最后那一两个 task 上。定位办法是按 key 统计分布找出热点,常用解法是加盐打散——给热点 key 拼上随机前缀,先做一次局部聚合,再去掉前缀按原始 key 汇总。加盐的本质是把一个超长任务拆成多个短任务。
基本信息
| 典型现象 | 少数 reduce 任务长尾,作业卡在最后几个 task |
|---|---|
| 定位手段 | 按 key 统计分布,倒序取 TOP N |
| 常用解法 | 加盐打散 + 两阶段聚合 |
| 兜底方案 | 热点 key 单独处理,其余走正常流程 |
| 高发场景 | GROUP BY 集中维度、大表 JOIN、去重统计 |
典型使用场景
- GROUP BY 高度集中的维度(如店铺、渠道)时出现长尾任务
- 大表 JOIN:关联键分布不均,或关联键存在大量空值
- 去重统计:单个 key 数据量过大,去重开销集中在一个任务上
- 分区写入倾斜:少数分区数据量异常,写文件耗时远超其他分区
- 作业耗时波动大:数据分布随业务变化,同一脚本时快时慢
定位方法
| 手段 | 做法 | 能看到什么 |
|---|---|---|
| key 分布统计 | 按 key 分组计数并倒序取 TOP | 哪些 key 是热点,差多少量级 |
| Spark UI | 看 stage 内各 task 的耗时与输入大小 | 是否存在长尾 task 及其所在分区 |
| 作业日志 | 对比各 reduce 的输入行数与耗时 | 倾斜发生在哪一步、哪个 key 上 |
定位热点
定位热点
SELECT store_id, count(1) AS cnt
FROM dwd_trade_order_di
WHERE dt='2026-10-13'
GROUP BY store_id
ORDER BY cnt DESC
LIMIT 20; -- 取 TOP20,看最高值与中位数差几个量级加盐两阶段聚合
加盐两阶段聚合
-- 第一阶段:给 key 拼上 0-9 的随机后缀,先把数据打散
INSERT OVERWRITE TABLE tmp_store_salted PARTITION (dt='2026-10-13')
SELECT concat(store_id,'_',cast(rand()*10 as int)) AS salt_key,
count(1) AS cnt, sum(pay_amt) AS pay_amt
FROM dwd_trade_order_di WHERE dt='2026-10-13'
GROUP BY concat(store_id,'_',cast(rand()*10 as int));
-- 第二阶段:去掉后缀,按原始 key 再汇总一次
SELECT split(salt_key,'_')[0] AS store_id, sum(cnt) AS cnt, sum(pay_amt) AS pay_amt
FROM tmp_store_salted WHERE dt='2026-10-13'
GROUP BY split(salt_key,'_')[0];加盐后必须在第二阶段按原始 key 再汇总一次,否则同一个门店会被拆成十份计数,结果看起来正常但数值翻倍。加盐份数也不是越多越好:份数过多会让第二阶段的聚合重新变成长尾。
只提高并行度解决不了倾斜:同一个 key 的数据始终落在同一个 reduce 分区里,任务数变多只会让那一个任务更孤立地拖住整个作业。