4. 数据倾斜的通用解决思路:调整并行度、重新设计Key、过滤异常数据、使用随机前缀
数据倾斜这个问题,说白了就是「忙的忙死,闲的闲死」。
我在一线处理过不少倾斜案例,有些是数据本身的问题,有些是业务逻辑埋的坑。但不管原因是什么,解决思路其实就那几板斧。今天我把最常用的四种方法掰开揉碎讲给你听。
4.1 调整并行度:最简单的第一招
很多人一遇到倾斜,第一反应就是「加大并行度」。这招有用吗?有点用,但治标不治本。
为什么?你想想看,并行度再大,如果某个Key的数据量占了一半,那这个Task还是得处理一半的数据。其他Task再闲也没用。
不过,在某些场景下调整并行度确实能缓解问题。比如数据分布虽然不均匀,但还没到「一核有难,多核围观」的程度。这时候适当提高并行度,能让每个Task处理的数据量降下来。
-- Spark中调整并行度
-- 设置shuffle分区数
SET spark.sql.shuffle.partitions = 200;
-- 或者针对特定操作调整
df.repartition(200, col("city")).write...
4.2 重新设计Key:从根源上解决问题
这是我最喜欢用的方法。说白了,倾斜的根源就是Key设计不合理。
举个例子,我在项目中遇到过按「省份」分组统计用户活跃度。结果呢?广东、江苏的数据量是西藏、青海的几百倍。这能不倾斜吗?
解决方案其实很简单——把Key拆细一点。比如把「省份+城市」作为组合Key,或者按时间维度再切分。
-- 不好的Key设计:按省份分组
SELECT province, COUNT(*) FROM user_logs GROUP BY province;
-- 好的Key设计:按省份+城市分组
SELECT province, city, COUNT(*) FROM user_logs GROUP BY province, city;
-- 或者按时间+省份
SELECT dt, province, COUNT(*) FROM user_logs GROUP BY dt, province;
4.3 过滤异常数据:把「捣乱分子」揪出来
嗯,这里要注意。很多时候倾斜是因为数据本身有问题。
我记得有一次做用户画像,发现某个Key的数据量特别大。一查,原来是某个爬虫程序把「null」当成了用户ID,结果几千万条数据都聚到了null这个Key上。
这种问题怎么处理?先过滤,再分析。
-- 过滤空值或异常值
SELECT
COALESCE(user_id, 'unknown') AS user_id,
COUNT(*)
FROM user_logs
WHERE user_id IS NOT NULL -- 过滤空值
AND user_id != '' -- 过滤空字符串
AND user_id != 'null' -- 过滤字符串"null"
GROUP BY user_id;
-- 或者直接排除异常Key
SELECT
user_id,
COUNT(*)
FROM user_logs
WHERE user_id NOT IN ('', 'null', '-1', '0') -- 排除常见异常值
GROUP BY user_id;
4.4 使用随机前缀:最灵活的「杀手锏」
这个方法,说白了就是给Key加点「盐」。让原本聚集在同一个Key上的数据,分散到多个Key上去。
具体怎么做?给Key加一个随机数前缀。比如原本的Key是「北京」,现在变成「0_北京」、「1_北京」、「2_北京」……这样数据就分散到多个Task了。
但要注意,这只是中间步骤。最后还得把加了前缀的数据聚合回来。
-- 第一步:加随机前缀,做局部聚合
SELECT
CONCAT(CAST(FLOOR(RAND() * 10) AS STRING), '_', city) AS city_with_prefix,
COUNT(*) AS partial_count
FROM user_logs
GROUP BY city_with_prefix;
-- 第二步:去掉前缀,做全局聚合
SELECT
SPLIT(city_with_prefix, '_')[1] AS city,
SUM(partial_count) AS total_count
FROM (
SELECT
CONCAT(CAST(FLOOR(RAND() * 10) AS STRING), '_', city) AS city_with_prefix,
COUNT(*) AS partial_count
FROM user_logs
GROUP BY city_with_prefix
) tmp
GROUP BY city;
4.5 四种方法怎么选?一张表说清楚
你可能会问,这么多方法,到底用哪个?我整理了一张表,方便你对照选择。
| 方法 | 适用场景 | 优点 | 缺点 | 推荐指数 |
|---|---|---|---|---|
| 调整并行度 | 数据分布轻度不均 | 实现简单,改动小 | 治标不治本,效果有限 | ★★☆☆☆ |
| 重新设计Key | Key粒度太粗 | 从根源解决问题 | 需要理解业务逻辑 | ★★★★★ |
| 过滤异常数据 | 存在脏数据或异常值 | 效果立竿见影 | 可能丢失有效数据 | ★★★★☆ |
| 使用随机前缀 | 单个Key数据量极大 | 灵活,适用性强 | 需要两步聚合,稍复杂 | ★★★★☆ |
好了,这四种方法讲完了。下次遇到数据倾斜,别慌,按这个思路一步步排查,大部分问题都能解决。