启用DRA的Spark集群中,如何合理设置SQL作业的分区数?
动态资源分配(DRA)下Spark SQL Shuffle分区数的合理设置
首先明确:默认的200分区数在DRA场景下基本没法适配动态变化的集群资源,完全不建议直接用。固定集群的2 * cores * instances公式也失效了,因为executor数量是动态波动的,得换思路来设置:
方案1:基于集群最大资源上限设置分区数
直接按集群能分配的最大executor数量来计算分区数,参考官方推荐的2-3倍核心数比例:
spark.sql.shuffle.partitions = 2~3 * spark.executor.cores * spark.dynamicAllocation.maxExecutors
比如集群最大允许100个executor、每个4核,那可以设成3*4*100=1200。这么做的好处是:
- 当DRA把executor拉满时,每个核心能分到2-3个任务,保证并行度最大化,不浪费资源
- 当executor数量较少时,虽然分区数偏多,但每个分区的数据量会变小,避免大分区导致的OOM,而Spark的任务调度开销其实可以忽略,远小于OOM带来的风险
方案2:开启自适应执行自动调整
开启spark.sql.adaptive.enabled=true(Spark 2.3+支持),Spark会根据实际数据量动态调整shuffle分区数:
- 自动合并小分区,避免过多小分区带来的调度开销
- 当数据量不均匀时,会拆分大分区,防止个别任务拖慢整体
- 配合
spark.sql.adaptive.shuffle.targetPostShuffleInputSize(默认64MB)和spark.sql.adaptive.shuffle.minNumPartitions(默认200),可以更精准控制分区大小和最小分区数
这种方案最省心,不需要手动计算分区数,尤其适合DRA场景,自适应机制会跟着资源和数据量动态调整。
方案3:代码层面动态计算分区数(进阶)
如果需要更精细化控制,可以在作业运行时,根据当前已分配的executor数量动态设置分区数:
// 获取当前executor数量(减去driver节点) val currentExecutors = spark.sparkContext.getExecutorMemoryStatus.size() - 1 // 按2倍核心数计算目标分区数 val targetPartitions = 2 * spark.conf.get("spark.executor.cores").toInt * currentExecutors // 应用到当前SparkSession spark.conf.set("spark.sql.shuffle.partitions", targetPartitions.toString)
注意要在触发shuffle的SQL语句执行前设置,若作业包含多个shuffle阶段,可能需要在每个阶段前重新计算(因为executor数量可能在作业运行中变化)。
关键注意点
- 别死抠公式,要结合实际数据量:理想的shuffle分区数据量在100-200MB之间,作业跑完后看Spark UI的Shuffle Read/Write,要是单个分区数据量远超200MB,就加分区数;要是远小于100MB,就减。
- 避免极端值:分区数别设得太小(比如低于核心数,导致并行度不够),也别设得太大(比如超过核心数的10倍,调度开销会明显上升)。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

