Spark SQL 性能调优方法与参数配置实战

Spark SQL 作业运行慢、数据倾斜、Shuffle 分区不合理、小文件过多等问题缺乏系统性调优方法论

袋鼠云
袋鼠云
经营分析

Spark SQL 性能调优可归结为三个核心原则:减少数据读取量、减少 Shuffle 数据传输量、消除数据倾斜。围绕这三点,配合 Spark 3.x 引入的 AQE(Adaptive Query Execution)自适应执行引擎,能解决绝大多数生产环境中的慢查询问题。

原则一:少读——减少数据读取量

作业运行慢的第一大原因往往是读了太多不该读的数据。以下是四个关键的减少读取量手段。

列裁剪。 永远用 SELECT 指定需要的列,不要使用 SELECT 星。Spark 在读取 Parquet 或 ORC 等列式存储格式时,只会读取查询中涉及的列,列裁剪可以将 I/O 量降低一个数量级。

分区裁剪。 确保查询条件能命中表的分区字段。例如按天分区的表查询时,WHERE 条件中带上日期列,Spark 会自动跳过不相关的分区目录。如果发现分区裁剪未生效,检查分区字段的数据类型是否与过滤值匹配——字符串类型的日期字段用整型值过滤不会触发裁剪。

谓词下推。 Spark 3.x 在 Parquet 和 ORC 读取器中默认开启谓词下推,将过滤条件推到文件层面执行。确认配置 spark.sql.parquet.enableVectorizedReader 为 true,以启用向量化读取器,在列式存储上获得数倍的读取加速。

数据格式选择。 如果源数据以 CSV 或 JSON 等行式格式存储,考虑将其转换为 Parquet 或 ORC 格式。列式存储不仅支持列裁剪和谓词下推,压缩率通常也比行式格式高 3 到 5 倍。

原则二:少传——减少 Shuffle 数据传输量

Shuffle 是 Spark 性能的瓶颈所在,数据在节点间的网络传输和磁盘读写开销巨大。

调整 Shuffle 分区数。 Spark 默认的 Shuffle 分区数为 200,对于小表来说分区过多导致大量小文件,对于大表来说分区过少导致单分区数据量过大。生产环境中的经验法则是:将 Shuffle 分区数设置为目标单分区大小 128MB 到 256MB 之间的值。可以通过 spark.sql.shuffle.partitions 调整。

Broadcast Join。 当一张表的大小小于广播阈值(默认 10MB)时,Spark 会将其广播到所有 Executor 上,避免 Shuffle Join。在 SQL 中可以通过 /*+ BROADCAST(table_name) */ 提示强制广播,或者调大 spark.sql.autoBroadcastJoinThreshold 以覆盖更多场景。

Bucket Join。 对于频繁 Join 的大表,可以按相同的 Join Key 进行分桶存储(CLUSTERED BY ... INTO N BUCKETS)。Bucket Join 在写入时已经按 Key 分好区,查询时不需要再做 Shuffle,可以将 Join 耗时缩短 50% 以上。

减少不必要的 Shuffle。 GROUP BY 和 DISTINCT 操作都会触发 Shuffle。如果业务允许,考虑用 SUM 配合 GROUP BY 替代 COUNT DISTINCT,或者将多次 Shuffle 合并为一次——Spark 的 WholeStageCodegen 机制会在条件允许时自动合并相邻的 Shuffle 算子。

数栈·多模态数据智能平台

袋鼠云面向 AI 时代打造的多模态数据智能平台,一站式覆盖数据存算、开发、治理、资产、服务与指标、分析、AI 应用,构建可见、可用、可管、可理解的私域数据底座。

了解产品

原则三:少倾斜——消除数据倾斜

数据倾斜是指 Shuffle 后个别分区的数据量远超其他分区,导致整个作业被最慢的那个分区拖住。

识别倾斜。 在 Spark UI 的 SQL 页面查看各 Stage 的 Task 耗时分布。如果某个 Task 的处理时间是中位数的 5 倍以上,基本可以判定存在数据倾斜。

加盐打散。 对倾斜的 Join Key 添加随机后缀(如将 key 替换为 key_1 到 key_N),将原本集中在同一个分区的数据分散到多个分区中。小表侧则需要将每个 Key 复制 N 份并分别加上后缀,使两边可以正常 Join。这是最经典的手动处理倾斜方案。

两阶段聚合。 对于 GROUP BY 导致的倾斜,先加随机前缀做一次局部聚合(Map-Side Combine),再去掉前缀做全局聚合。这种方法将一次大 Shuffle 拆成两次小 Shuffle,有效缓解倾斜问题。

Spark 3.x AQE 核心特性

AQE 是 Spark 3.x 中最重要的性能优化之一,它让查询计划在运行时根据实际数据统计信息自动调整。

动态合并 Shuffle 分区。 AQE 会在 Shuffle 完成后自动合并过小的分区。即使初始分区数设置偏大,AQE 也能将小于阈值的相邻分区合并,避免过多小文件带来的额外 I/O 开销。配置项为 spark.sql.adaptive.coalescePartitions.enabled,默认开启。

动态切换 Join 策略。 Shuffle 完成后,AQE 会重新评估各表的实际大小。如果某张表在运行时的实际大小小于广播阈值(可能因为过滤条件大幅减少了数据量),AQE 会自动将 Sort Merge Join 切换为 Broadcast Join,省掉一次 Shuffle。

动态优化倾斜 Join。 AQE 可以自动检测 Shuffle 后数据倾斜的分区,并将其拆分成更小的子分区单独处理。无需手动加盐打散,配置 spark.sql.adaptive.skewJoin.enabled 为 true 即可开启。

经营分析

围绕目标、预算、执行、异常与归因,构建经营分析、过程监控、问题定位与决策闭环。

了解解决方案

参数配置实战示例

以下是一组经过生产验证的 Spark SQL 调优参数基线配置:

spark.sql.adaptive.enabled 设为 true,启用 AQE 自适应执行引擎。

spark.sql.adaptive.coalescePartitions.minPartitionSize 设为 64MB,控制分区合并的最小阈值。

spark.sql.adaptive.skewJoin.enabled 设为 true,启用倾斜 Join 自动优化。

spark.sql.shuffle.partitions 设为 500,作为初始 Shuffle 分区数,后续由 AQE 动态调整。

spark.sql.autoBroadcastJoinThreshold 设为 50MB,扩大自动广播的表大小上限。

spark.sql.parquet.enableVectorizedReader 设为 true,启用 Parquet 向量化读取。

spark.serializer 设为 org.apache.spark.serializer.KryoSerializer,使用 Kryo 序列化器减少 Shuffle 数据体积。

这些参数作为起步基线,在实际业务中还需要根据作业的数据量特征和集群资源情况进行微调。建议每次只调整一到两个参数,通过对比 Spark UI 中 Stage 耗时和 Task 数据量来评估效果,避免同时调整过多参数导致难以定位性能变化的原因。

小结

Spark SQL 调优的核心在于围绕"少读、少传、少倾斜"三原则,逐层排查并优化作业的每个环节。Spark 3.x 的 AQE 特性大幅降低了手动调优的门槛,但理解底层原理仍然必要——AQE 能自动处理常规场景,极端的数据倾斜和资源瓶颈仍需人工介入。参数配置没有银弹,持续监控和渐进式调优才是长期有效的策略。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信