某个Task比其他慢了10倍——Spark数据倾斜怎么解决?
先说结论
解决倾斜的优先级:AQE自动处理 > 加盐打散 > 广播小表 > 拆分热点Key。先试最简单的,不行再上复杂的。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
Spark的数据倾斜和Hive类似,但解决方案更丰富。我做了9年大数据,倾斜是遇到最多的性能问题。今天把Spark里解决倾斜的四种方案按优先级讲清楚。
怎么发现倾斜?
在Spark UI的Stage详情页,看每个Task的“Shuffle Read Size”。如果多数Task只读了10MB,但有一个Task读了5GB,那就是倾斜。
也可以用代码检查:
-- 检查Key分布,找出倾斜Key
SELECT join_key, COUNT(1) AS cnt
FROM table_a
GROUP BY join_key
ORDER BY cnt DESC
LIMIT 20;
方案一:开启AQE自动处理(最简单)
Spark 3.0+的AQE能自动检测倾斜分区并拆分。这是最简单的方案,90%的倾斜场景可以解决。配置见上一篇。
方案二:加盐打散(两阶段聚合)
用于GROUP BY倾斜场景。核心思想:先给Key加一个随机后缀做第一次聚合(打散到多个Task),再去掉后缀做第二次聚合。
// 第一次聚合:加盐打散
val salted = df.withColumn("salted_key",
concat(col("country"), lit("_"), (rand() * 10).cast("int")))
val partial = salted.groupBy("salted_key")
.agg(sum("amount").as("partial_sum"), count("*").as("partial_cnt"))
// 第二次聚合:去盐汇总
val result = partial
.withColumn("country", split(col("salted_key"), "_")(0))
.groupBy("country")
.agg(sum("partial_sum").as("total_amount"),
sum("partial_cnt").as("total_count"))
方案三:Broadcast小表避免Shuffle
如果Join倾斜且其中一张表足够小,直接用Broadcast Join就可以避免Shuffle,从根本上消除倾斜。这是最优雅的解法,但前提是有一张小表。
方案四:拆分热点Key单独处理
把倾斜的热点Key和非热点Key分开处理,热点Key用Broadcast Join,非热点Key用普速SORT Merge Join,最后UNION ALL合并结果。
实战经验
我在出海硬件公司做用户行为分析时,有个按device_id做GROUP BY的作业,某个测试设备的device_id的数据量占总量的30%(自动化测试产生的大量数据)。开启AQE后自动解决,作业从40分钟降到8分钟。
总结
倾斜解决优先级:AQE自动 > 加盐打散 > Broadcast小表 > 拆分热点Key。先用最简单的,不行再上复杂的。面试时能说清楚“怎么发现倾斜 + 多种解决方案 + 实战案例”,就很稳。
下一篇聊一个很多人志题的问题:DataFrame和RDD到底用哪个?为什么现在几乎没人用RDD了?
你们生产环境的倾斜是用什么方案解决的?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长