首页/数据/数据工程与 ETL 速查/数据倾斜的识别与处理
管道基础52 条

数据倾斜的识别与处理

个别 key 数据量远超其他 key 时,单个 task 成为长尾:表现为多数 task 秒级完成、个别 task 长时间运行甚至 OOM。处理手段包括加盐两阶段聚合、广播小表、拆分热点 key。

数据倾斜是什么

数据倾斜指参与同一类计算的数据在不同 key 之间分布极不均匀:绝大多数 key 只有几百条记录,个别 key 却有上千万条。分区与 task 是按 key 划分的,于是这些 key 落在的 task 成为长尾,拖住整个作业——其他 task 早已跑完,它还在读写磁盘。

怎么判断是倾斜还是别的问题

在作业监控里看 task 耗时分布:多数 task 秒级完成、少数 task 长时间运行甚至内存溢出,是典型倾斜特征。另一种是分区倾斜:源文件大小严重不均(例如一个大文件配若干小文件),表现是所有 task 都慢,需要的是重分区而不是处理热点键。

基本信息

表现任务长尾、个别 task 内存溢出、spill 严重
成因热点键、空键、数据分布天然不均、文件大小不均
定位手段task 耗时分布、按 key 计数、分区大小分布
处理次序先过滤无效数据,再考虑打散或广播
平台提示Spark AQE 可自动处理部分 join 倾斜(默认开启)

倾斜类型与对策

类型典型场景对策
分组聚合倾斜少数 key 记录极多,如默认值、空值过滤无效 key,或两阶段加盐聚合
join 倾斜大表关联小表且小表未广播把小表广播,或对热 key 单独处理
join 大键倾斜两张大表按热点键关联开启 AQE 倾斜 join 或手动拆分热键
分区/文件倾斜源文件大小不均、分区数远大于数据量重分区,调整并行度与文件切分

常见处理手段

  • 先过滤:把空键、默认值、测试账号等无效数据剔除,往往能直接消掉大部分倾斜
  • 加盐两阶段聚合:给热键加随机后缀做局部聚合,再去后缀合并
  • 广播小表:小表放进内存,避免按 key 重新分布大表数据
  • 单独处理热键:把热点 key 拎出来单独算,其余走常规路径,最后合并
  • 调整并行度与重分区:应对文件级或分区级倾斜
  • 开启 AQE:让引擎在运行期检测并拆分过大的分区

两阶段加盐聚合

两阶段加盐聚合
-- 阶段一:对热键加随机后缀,把同一个键打散到多个 task 做局部聚合
CREATE TABLE tmp_agg AS
SELECT concat(k, '#', cast(floor(rand() * 10) as string)) AS k_salt,
       sum(v) AS part_sum
FROM src
GROUP BY concat(k, '#', cast(floor(rand() * 10) as string));

-- 阶段二:去掉后缀把局部结果合并起来
SELECT split(k_salt, '#')[0] AS k, sum(part_sum) AS total
FROM tmp_agg
GROUP BY split(k_salt, '#')[0];

广播小表与开启 AQE

广播小表与开启 AQE
-- 手动提示广播:小表进内存,避免大表做 shuffle 重分布
SELECT /*+ BROADCAST(c) */ o.order_id, c.channel_name
FROM dwd_order o LEFT JOIN dim_channel c ON o.channel_code = c.channel_code;

-- 运行期自适应:自动合并小分区并拆分倾斜分区(Spark 3.2 起默认开启)
SET spark.sql.adaptive.enabled = true;
SET spark.sql.adaptive.skewJoin.enabled = true;
SET spark.sql.autoBroadcastJoinThreshold = 10485760;   -- 广播阈值,默认 10MB
加盐只能解决「计算被打散」,不能解决「结果本身不完整」:如果业务上这些键真的需要集中处理,加盐后仍需一次收敛步骤,整体耗时不会消失,只是从单点长尾变成可并行。
加盐用的分隔符若与业务 key 本身冲突(key 里含有同一个字符),阶段二的拆分就会解析出错误的键,结果与预期完全不符;选一个业务数据中不会出现的分隔符。

易错点

  • 加盐分隔符与业务 key 冲突,阶段二解析出错误的分组键,结果整体错乱且不易察觉
  • null 键参与分组时会集中到一个 task,形成最大的热点,分组前先过滤或改造 null 值
  • 把分区倾斜当成数据倾斜处理:文件大小不均导致的问题靠加盐解决不了,需要重分区
  • 直接调大并行度应对倾斜:倾斜的是分布不是总量,并行度提高后长尾 task 依然存在

数据来源与参考

  • Apache Spark 官方文档:Performance Tuning(Data Skew 与 AQE)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌