数据倾斜是 SQL 大数据处理中常见且棘手的问题,它会导致计算资源分配不均,某些任务节点负载过高,从而拖慢整体执行效率甚至引发 OOM(内存溢出)。在实际开发中,我们经常需要分析 SQL 执行计划,定位倾斜数据并优化查询逻辑。近期,我在一个用户行为分析项目中遇到了严重的数据倾斜问题,借助 DeepSeek 的代码分析与优化建议,成功提升了查询性能。本文将分享这一过程中的具体场景、优化思路和关键代码。
项目中有一个用户事件表 user_events
,存储了用户的点击、浏览等行为日志,每天数据量约 10 亿条。某天收到报警,一个日常统计 SQL 运行时间从平时的 10 分钟突然延长到 2 小时以上。该 SQL 用于统计每个类别(category)下活跃用户数(UV)和总点击量(PV),简化如下:
SELECT
category,
COUNT(DISTINCT user_id) AS uv,
COUNT(1) AS pv
FROM user_events
WHERE dt = '20230901'
GROUP BY category;
首先检查执行计划(Explain),发现最后一个 Stage 有 2000 个任务,其中 1990 个在几秒内完成,但剩余 10 个任务运行了超过 1 小时。明显存在数据倾斜。
我将执行计划和表结构信息输入 DeepSeek,询问可能的数据倾斜原因。DeepSeek 提示:
查询 category 的数据分布,确认倾斜存在:
SELECT
category,
COUNT(1) AS record_count
FROM user_events
WHERE dt = '20230901'
GROUP BY category
ORDER BY record_count DESC
LIMIT 10;
结果发现,top 1 的 category(“首页”)有 5 亿条记录,而大部分 category 只有几十万条。
这是处理 GROUP BY 倾斜的常用方法。首先在 map 端进行局部聚合,减少 shuffle 数据量:
SELECT
category,
SUM(partial_uv) AS uv,
SUM(partial_pv) AS pv
FROM (
SELECT
category,
COUNT(DISTINCT user_id) AS partial_uv,
COUNT(1) AS partial_pv
FROM user_events
WHERE dt = '20230901'
GROUP BY category, user_id -- 先按 category 和 user_id 聚合
) t
GROUP BY category;
优化效果:
将热点键和非热点键分开处理,最后合并结果:
-- 创建临时表存储非热点数据
CREATE TABLE tmp_non_hotspot AS
SELECT
category,
user_id,
COUNT(1) AS pv
FROM user_events
WHERE dt = '20230901'
AND category != '首页' -- 排除热点键
GROUP BY category, user_id;
-- 处理热点键“首页”,添加随机后缀打散
CREATE TABLE tmp_hotspot AS
SELECT
CONCAT(category, '_', CAST(RAND() * 100 AS INT)) AS category_suffix,
user_id,
COUNT(1) AS pv
FROM user_events
WHERE dt = '20230901'
AND category = '首页'
GROUP BY CONCAT(category, '_', CAST(RAND() * 100 AS INT)), user_id;
-- 合并结果
SELECT
category,
SUM(uv) AS uv,
SUM(pv) AS pv
FROM (
-- 非热点数据
SELECT
category,
COUNT(DISTINCT user_id) AS uv,
SUM(pv) AS pv
FROM tmp_non_hotspot
GROUP BY category
UNION ALL
-- 热点数据(去除随机后缀后聚合)
SELECT
SPLIT(category_suffix, '_')[0] AS category,
COUNT(DISTINCT user_id) AS uv,
SUM(pv) AS pv
FROM tmp_hotspot
GROUP BY SPLIT(category_suffix, '_')[0]
) t
GROUP BY category;
优化效果:
如果计算引擎是 Spark,可以通过配置自动处理倾斜:
-- 开启倾斜优化
SET spark.sql.adaptive.skewedJoin.enabled=true;
SET spark.sql.adaptive.skewedPartitionFactor=10;
SET spark.sql.adaptive.skewedPartitionThresholdInBytes=256MB;
-- 原始 SQL
SELECT
category,
COUNT(DISTINCT user_id) AS uv,
COUNT(1) AS pv
FROM user_events
WHERE dt = '20230901'
GROUP BY category;
优化效果:
综合权衡后,我们选择了方案 2(倾斜键分离),因为当前集群 Spark 版本较低(2.3),不支持 AQE。优化后,作业运行时间从 2 小时降至 15 分钟。
通过这次实践,我深刻体会到:工具(DeepSeek)能加速问题定位和方案设计,但最终优化效果取决于对业务数据和技术框架的理解。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。