GROUP BY慢的本质是Shuffle瓶颈,因数据需按key重分区、拉取、聚合,引发网络传输、磁盘IO与排序开销;key倾斜时更会拖慢整个stage。

为什么GROUP BY慢?本质是Shuffle瓶颈
GROUP BY 在 Spark SQL 中必然触发 Shuffle,数据要按 key 重新分区、拉取、聚合。慢不是因为逻辑复杂,而是网络传输 + 磁盘落盘 + 排序开销叠加导致的。尤其当分组 key 分布不均(比如某个 user_id 占 80% 数据),单个 task 就会卡住整个 stage。
必须调的三个Shuffle参数
别一上来就改十多个配置,先盯住这三个最直接生效的:
-
spark.sql.shuffle.partitions:默认 200,但实际应接近输入数据总行数的平方根。例如读入 10 亿行,设为1000比200更合理;太小会导致单 task 负载过重,太大则小 task 过多、调度开销上升 -
spark.shuffle.file.buffer:默认 32KB,建议调到64KB或128KB。增大缓冲区能减少磁盘小文件写入次数,对 GROUP BY 这类高频 shuffle 场景效果明显 -
spark.reducer.maxSizeInFlight:默认 48MB,可增至96MB。它控制 reducer 端单次拉取的 shuffle 数据量,调大后网络吞吐更稳,避免反复拉取造成的延迟毛刺
GROUP BY前必须做的两件事
参数只是辅助,真正省时间的是减少参与 shuffle 的数据量:
- 用子查询提前过滤:把
WHERE下推到GROUP BY之前,比如SELECT city, COUNT(*) FROM (SELECT * FROM logs WHERE dt = '2026-09-17') t GROUP BY city,而不是在 GROUP BY 后再 WHERE - 确认 key 是否含大量 NULL:NULL 值在多数版本中会被 hash 到同一个 partition,极易倾斜。用
COALESCE(city, 'unknown')替换,或直接WHERE city IS NOT NULL
容易被忽略的隐性陷阱
很多人开了 spark.sql.adaptive.enabled=true 就以为万事大吉,但其实它只在 runtime 生效,且依赖 shuffle 阶段已触发——如果 GROUP BY 前的数据源本身是小文件堆叠(比如几千个 1MB Parquet 文件),first stage 就可能因 task 数量爆炸而卡死,AQE 根本没机会介入。此时得先合并小文件,或显式用 repartition(100) 控制输入并行度,再让 GROUP BY 走上正轨。

















