Spark SQL 自适应执行优化引擎
背景
Adaptive Execution 将可以根据执行过程中的中间数据优化后续执行,从而提高整体执行效率。核心在于两点
- 执行计划可动态调整
- 调整的依据是中间结果的精确统计信息
spark 3.0 正式发布 自适应查询执行(Adaptive Query Execution)
如何设置合适的shuffle partition数量?
在Spark SQL中, shufflepartition数可以通过参数spark.sql.shuffle.partition来设置,默认值是200。
如果partition太小,单个任务处理的数据量会越大,在内存有限的情况,就会写文件,降低性能,还会oom
如果partition太大,每个处理任务数据量很小,很快结束,导致spark调度负担变大,中间临时文件多
spark sql 最佳执行计划
Spark SQL的Catalyst优化器的核心工作就是选择最佳的执行计划,主要依靠:
早起基于规则的优化器RBO,spark2.2 加入基于代价的优化CBO
执行计划在计划阶段确定后,不会改变,如果能够获取运行时信息,就可能得到一个更加的执行计划
数据倾斜如何处理
数据倾斜是指某一个partition的数据量远远大于其它partition的数据,导致个别任务的运行时间远远大于其它任务,因此拖累了整个SQL的运行时间。
常见手段:
- 增加shuffle partition数量,让热点partition的数据分散一些,但是对于同一个key没有作用
- 增加 BroadcastHashJoin的阈值,在某些场景下可以把SortMergeJoin转化成BroadcastHashJoin而避免shuffle产生的数据倾斜。
- 手动过滤倾斜key,加入前缀,join表也对key膨胀处理,再join
spark 能否运行时自动处理join中的数据倾斜:自适应执行架构
基础流程
sql -> 解析 -> 逻辑计划 -> 物理计划 -> rdd -> job -> dag -> stage -> task run
一旦执行计划确定,无法更新
自适应划分依据
- 按照每个reducer处理partition数据内存大小分,每个64m
- 按照每个reducer处理partition数据条数分,100000条
动态调整执行计划
在运行时动态调整join的策略,在满足条件的情况下,即一张表小于Broadcast阈值,可以将SortMergeJoin转化成BroadcastHashJoin。
- SortMergeJoin,每个reducer通过网络shuffle读取属于自己的数据;会出现不同程度的数据倾斜问题;
- BroadcastHashJoin,每一个reducer读取一个mapper的整个shuffle output文件,shuffle读变成了本地读取,没有数据通过网络传输;数据量一般比较均匀,也就避免了倾斜;
动态处理数据倾斜
在运行时很容易地检测出有数据倾斜的partition,当执行某个stage时,我们收集该stage每个mapper 的shuffle数据大小和记录条数
如果某一个partition的数据量或者记录条数超过中位数的N倍,并且大于某个预先配置的阈值,我们就认为这是一个数据倾斜的partition,需要进行特殊的处理
SparkSQL自适应执行
阿里云E-MapReduce 3.13.0及后续版本的SparkSQL支持自适应执行功能,可以用来解决Reduce个数的动态调整、数据倾斜和执行计划的动态优化问题。
使用限制
本文针对SparkSQL自适应执行涉及到的参数适用于Spark 2.x。如果您使用的是Spark 3.x,请参见Adaptive Query Execution。
解决问题
SparkSQL自适应执行解决以下问题:
- Shuffle partition个数
目前SparkSQL中reduce阶段的task个数取决于固定参数spark.sql.shuffle.partition(默认值200),一个作业一旦设置了该参数,运行过程中的所有阶段的reduce个数都是同一个值。
而对于不同的作业,以及同一个作业内的不同reduce阶段,实际的数据量大小可能相差很大,例如reduce阶段要处理的数据可能是10 MB,也有可能是100 GB,如果使用同一个值对实际运行效率会产生很大影响,例如10 MB的数据一个task就可以解决,如果spark.sql.shuffle.partition使用默认值200的话,那么10 MB的数据就要被分成200个task处理,增加了调度开销,影响运行效率。
SparkSQL自适应框架可以通过设置Shuffle partition的上下限区间,在这个区间内对不同作业不同阶段的reduce个数进行动态调整。
通过区间的设置,一方面可以大大减少调优的成本(不需要找到一个固定值),另一方面同一个作业内部不同reduce阶段的reduce个数也能动态调整。
涉及参数如下。
属性名称 | 默认值 | 描述 |
---|---|---|
spark.sql.adaptive.enabled | false | 自适应执行框架的开关。倾斜处理开关 |
spark.sql.adaptive.minNumPostShufflePartitions | 1 | reduce个数区间最小值。 v2.4 有 3.0 已经去掉 动态调整 reduce 个数的 partition 条数依据。如设置 20000000,则 reduce 阶段每个 task 最少处理 20000000 条的数据。默认值为 20000000。 |
spark.sql.adaptive.maxNumPostShufflePartitions | 500 | reduce个数区间最大值。 |
spark.sql.adaptive.shuffle.targetPostShuffleInputSize | 67108864 | 动态调整reduce个数的partition大小依据,如果设置为64 MB,则reduce阶段每个task最少处理64 MB的数据。 当mapper端两个partition的数据合并后数据量小于targetPostShuffleInputSize时,spark会将两个partition进行合并到一个reducer端进行处理。 |
spark.sql.adaptive.shuffle.targetPostShuffleRowCount | 20000000 | 动态调整reduce个数的partition条数依据,如设置20000000则reduce阶段每个task最少处理20000000条的数据。 |
spark.sql.adaptive.forceApply | 自适应执行在没有需要shuffle或者子查询的时候将不适用,当设为true始终使用 | |
spark.sql.adaptive.coalescePartitions.enabled | 是否开启合并小数据分区默认开启,调优策略之一 | |
- 数据倾斜
JOIN中会经常碰到数据倾斜的场景,导致某些task处理的数据过多,出现很严重的长尾。目前SparkSQL没有对倾斜的数据进行相关的优化处理。
SparkSQL自适应框架可以根据预先的配置在作业运行过程中自动检测是否出现倾斜,并对检测到的倾斜进行优化处理。
优化的主要逻辑是对倾斜的partition进行拆分由多个task来进行处理,最后通过UNION进行结果合并。
支持的JOIN类型如下。
JOIN类型 | 描述 |
---|---|
Inner | 左或右表均可处理倾斜。 |
Cross | 左或右表均可处理倾斜。 |
LeftSemi | 只对左表处理倾斜。 |
LeftAnti | 只对左表处理倾斜。 |
LeftOuter | 只对左表处理倾斜。 |
RightOuter | 只对右表处理倾斜。 |
涉及参数如下。
属性名称 | 默认值 | 备注 |
---|---|---|
spark.sql.adaptive.enabled | false | 自适应执行框架的开关。 |
spark.sql.adaptive.skewedJoin.enabled | false | 倾斜处理开关。 自动倾斜处理,处理 sort-merge join中的倾斜数据 |
spark.sql.adaptive.skewedPartitionFactor | 10 | 当一个partition的size大于该值(所有parititon大小的中位数) 且大于spark.sql.adaptive.skewedPartitionSizeThreshold,或者parition的条数大于该值(所有parititon条数的中位数)且大于 spark.sql.adaptive.skewedPartitionRowCountThreshold, 才会被当做倾斜的partition进行相应的处理。 |
spark.sql.adaptive.skewedPartitionSizeThreshold | 67108864 | 倾斜的partition大小不能小于该值。 |
spark.sql.adaptive.skewedPartitionRowCountThreshold | 10000000 | 倾斜的partition条数不能小于该值。 |
spark.shuffle.statistics.verbose | false | 打开后MapStatus会采集每个partition条数的信息,用于倾斜处理。 |
spark.sql.adaptive.skewJoin.skewedPartitionFactor | 判断分区是否是倾斜分区的比例 当一个 partition 的 size 大小大于该值(所有 parititon 大小的中位数)且大于spark.sql.adaptive.skewedPartitionSizeThreshold,或者 parition 的条数大于该值(所有 parititon 条数的中位数)且大于 spark.sql.adaptive.skewedPartitionRowCountThreshold,才会被当做倾斜的 partition 进行相应的处理。默认值为 10 | |
spark.sql.adaptive.coalescePartitions.enabled | 是否开启合并小数据分区 默认开启,调优策略之一 |
- Runtime执行计划优化
SparkSQL的Catalyst优化器会将SQL语句转换成物理执行计划,然后真正运行物理执行计划。但是Catalyst转换物理执行计划的过程中,由于缺少Statistics统计信息,或者Statistics统计信息不准等原因,实际转换的物理执行计划可能并不是最优的,例如转换为SortMergeJoinExec,但实际BroadcastJoin更合适。
SparkSQL自适应执行框架会在物理执行计划真正运行的过程中,动态的根据shuffle阶段shuffle write的实际数据大小,来调整是否可以用 BroadcastJoin来代替SortMergeJoin,提高运行效率。
涉及参数如下。
属性名称 | 默认值 | 备注 |
---|---|---|
spark.sql.adaptive.enabled | false | 自适应执行框架的开关。 |
spark.sql.adaptive.join.enabled | true | 开关。 |
spark.sql.adaptiveBroadcastJoinThreshold | 为spark.sql.autoBroadcastJoinThreshold设置的参数值 | 运行过程中用于判断是否满足BroadcastJoin条件。 |