spark3新特性之动态分区裁剪

本文介绍了 Spark 中的静态分区裁剪和动态分区裁剪。静态分区裁剪可减少数据扫描和磁盘 I/O。动态分区裁剪是 Spark 3.0 的特性,在逻辑计划和物理计划阶段有不同优化方式,还说明了其适用条件,如相关参数设置、表类型和 Join 类型等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spark3.0为我们带来了许多令人期待的特性。Spark中的静态分区裁剪在介绍动态分区裁剪之前,有必要对Spark中的静态分区裁剪进行介绍。因此,在这种情况下,我们不能再应用静态分区裁剪,因为filter条件在join表的一侧,而对裁剪有用的表在Join的另一侧。Spark将这个查询转化为一种易于理解的形式,我们称它为查询的逻辑计划。物理计划阶段优化如果维度表很小,那么Spark很可能会以broadcasthashjoin的形式执行这个Join。

Spark 3.0 为我们带来了许多令人期待的特性。动态分区裁剪(dynamic partition pruning)就是其中之一。本文将通过图文的形式来带大家理解什么是动态分区裁剪。

Spark 中的静态分区裁剪

在介绍动态分区裁剪之前,有必要对 Spark 中的静态分区裁剪进行介绍。在标准数据库术语中,裁剪意味着优化器将避免读取不包含我们正在查找的数据的文件。例如我们有以下的查询 SQL:

1

<ol><li>Select * from iteblog.Students where subject = 'English'; </li></ol>

在这个简单的查询中,我们试图匹配和识别 Students 表中 subject = English 的记录。比较愚蠢的做法是先把数据全部 scan 出来,然后再使用 subject = 'English' 去过滤。如下图所示:


 

比较好的实现是查询优化器将过滤器下推到数据源,以便能够避免扫描整个数据集,Spark 就是这么来做的,如下图所示:


 

在静态分区裁剪技术中,我们的表首先是分区的,分区过滤下推的思想和上面的 filter push down 一致。因为在这种情况下,如果我们的查询有一个针对分区列的过滤,那么在实际的查询中可以跳过很多不必要的分区,从而大大减少数据的扫描,减少磁盘I/O,从而提升计算的性能。

然而,在现实中,我们的查询语句不会是这么简单的。通常情况下,我们会有多张维表,小表需要与大的事实表进行 join。因此,在这种情况下,我们不能再应用静态分区裁剪,因为 filter 条件在 join 表的一侧,而对裁剪有用的表在 Join 的另一侧。比如我们有以下的查询语句:

1

<ol><li>Select * from iteblog.Students join iteblog.DailyRoutine  </li><li>where iteblog.DailyRoutine.subject = 'English'; </li></ol>

对于上面的查询,比较垃圾的查询引擎最后的执行计划如下:


 

它把两张表的数据进行关联,然后再过滤。在数据量比较大的情况下效率可想而知。一些比较好的计算引擎可以进行一些优化,比如:


 

其能够在一张表里面先过滤一些无用的数据,再进行 Join,效率自然比前面一种好。但是如果是我们人来弄,其实我们可以把 subject = 'English' 过滤条件下推到 iteblog.Students 表里面,这个正是 Spark 3.0 给我们带来的动态分区裁剪优化。

动态分区裁剪

在 Spark SQL 中,用户通常用他们喜欢的编程语言并选择他们喜欢的 API 来提交查询,这也就是为什么有 Dataframes 和 DataSet。Spark 将这个查询转化为一种易于理解的形式,我们称它为查询的逻辑计划(logical plan)。在此阶段,Spark 通过应用一组基于规则(rule based)的转换(如列修剪、常量折叠、算子下推)来优化逻辑计划。然后,它才会进入查询的实际物理计划(physical planning)。在物理规划阶段 Spark 生成一个可执行的计划(executable plan),该计划将计算分布在集群中。本文我将解释如何在逻辑计划阶段实现动态分区修剪。然后,我们将研究如何在物理计划阶段中进一步优化它。


 

逻辑计划阶段优化

假设我们有一个具有多个分区的事实表(fact table),为了方便说明,我们用不同颜色代表不同的分区。另外,我们还有一个比较小的维度表(dimension table),我们的维度表不是分区表。然后我们在这些数据集上进行典型的扫描操作。在我们的例子里面,假设我们只读取维度表里面的两行数据,而这两行数据其实对于另外一张表的两个分区。所以最后执行 Join 操作时,带有分区的事实表只需要读取两个分区的数据就可以。

因此,我们不需要实际扫描整个事实表。为了做到这种优化,一种简单的方法是通过维度表构造出一个过滤子查询(比如上面例子为 select subject from iteblog.DailyRoutine where subject = 'English'),然后在扫描事实表之前加上这个过滤子查询。

通过这种方式,我们在逻辑计划阶段就知道事实表需要扫描哪些分区。


 

但是,上面的物理计划执行起来还是比较低效。因为里面有重复的子查询,我们需要找出一种方法来消除这个重复的子查询。为了做到这一点,Spark 在物理计划阶段做了一些优化。

物理计划阶段优化

如果维度表很小,那么 Spark 很可能会以 broadcast hash join 的形式执行这个 Join。Broadcast Hash Join 的实现是将小表的数据广播(broadcast)到 Spark 所有的 Executor 端,这个广播过程和我们自己去广播数据没什么区别,先利用 collect 算子将小表的数据从 Executor 端拉到 Driver 端,然后在 Driver 端调用 sparkContext.broadcast 广播到所有 Executor 端;另一方面,大表也会构建 hash table(称为 build relation),之后在 Executor 端这个广播出去的数据会和大表的对应的分区进行 Join 操作,这种 Join 策略避免了 Shuffle 操作。具体如下:


 

我们已经知道了 broadcast hash join 实现原理。其实动态分区裁剪优化就是在 broadcast hash join 中大表进行 build relation 的时候拿到维度表的广播结果(broadcast results),然后在 build relation 的时候(Scan 前)进行动态过滤,从而达到避免扫描无用的数据效果。具体如下:


 

好了,以上就是动态分区裁剪在逻辑计划和物理计划的优化。

动态分区裁剪适用条件

并不是什么查询都会启用动态裁剪优化的,必须满足以下几个条件:

spark.sql.optimizer.dynamicPartitionPruning.enabled 参数必须设置为 true,不过这个值默认就是启用的; 需要裁减的表必须是分区表,而且分区字段必须在 join 的 on 条件里面; Join 类型必须是 INNER, LEFT SEMI (左表是分区表), LEFT OUTER (右表是分区表), or RIGHT OUTER (左表是分区表)。 满足上面的条件也不一定会触发动态分区裁减,还必须满足 spark.sql.optimizer.dynamicPartitionPruning.useStats 和 spark.sql.optimizer.dynamicPartitionPruning.fallbackFilterRatio 两个参数综合评估出一个进行动态分区裁减是否有益的值,满足了才会进行动态分区裁减。评估函数实现请参见 org.apache.spark.sql.dynamicpruning.PartitionPruning#pruningHasBenefit。

本文主要翻译自:https://blog.knoldus.com/dynamic-partition-pruning-in-spark-3-0/

参考:Spark3新特性之动态分区裁剪(Dynamic Partition Pruning,DPP)_spark动态分区裁剪_今天努力了吗??的博客-CSDN博客

<think>嗯,用户问的是Spark动态分区裁剪(DPP)和谓词下推的区别及工作原理。我需要先回忆这两个概念的定义和它们在Spark中的作用。 首先,动态分区裁剪Spark 3.0引入的一个优化特性,主要用于处理星型模型中的事实表和维度表的关联查询。当维度表经过过滤后,DPP可以将过滤后的分区信息应用到事实表上,从而减少需要扫描的数据量。比如,当事实表按日期分区,而查询中关联了过滤后的维度表日期,DPP会只扫描事实表中符合条件的分区,避免全表扫描。 然后,谓词下推是一个更通用的优化技术,属于逻辑优化阶段。它把过滤条件下推到数据源附近,尽早减少处理的数据量。比如在JOIN之前先进行WHERE条件的过滤,这样参与JOIN的数据就变少了。根据引用[4],不同的引擎对包含OR操作符的条件处理可能不同,但核心思想都是尽早过滤。 接下来要对比它们的区别。首先是应用场景:DPP专门针对分区表,特别是星型模型中的关联查询;而谓词下推适用于各种数据源,不限于分区表。然后是优化阶段,DPP可能是在物理优化阶段,而谓词下推属于逻辑优化。还有数据依赖,DPP需要分区键和过滤键一致,而谓词下推更灵活。作用范围上,DPP减少扫描的分区数,谓词下推减少单分区内的数据量。 工作原理部分需要分别解释。DPP的工作流程可能是在执行时根据维度表的过滤结果动态生成事实表的分区列表,从而剪裁掉不需要的分区。而谓词下推是在逻辑计划优化阶段将过滤条件移动到靠近数据源的位置,比如在扫描表之后立即应用过滤条件。 另外,根据引用[2],Catalyst优化器在逻辑优化阶段会做谓词下推,而物理优化阶段可能涉及DPP。引用[3]提到谓词下推常与其他优化技术结合使用,而DPP可能更多是单独的分区剪裁策略。 用户可能需要知道它们如何互补,比如在星型查询中,DPP减少分区,谓词下推进一步减少每个分区的数据量。这样两者的结合可以最大化性能提升。 需要确保回答结构清晰,分点说明区别和工作原理,并引用相关文献。同时,生成的相关问题要围绕这两个技术展开,帮助用户深入理解。</think>### Spark动态分区裁剪(DPP)与谓词下推的区别及工作原理 #### 一、核心区别 | **特性** | **动态分区裁剪(DPP)** | **谓词下推** | |------------------------|-----------------------------------------------|-------------------------------------------------| | **应用场景** | 分区表关联查询(如星型模型事实表与维度表)[^1] | 所有数据源(包括非分区表)[^2] | | **优化阶段** | 物理优化阶段(基于运行时统计信息) | 逻辑优化阶段(基于静态规则)[^3] | | **数据依赖** | 要求分区键与过滤键一致 | 无分区键要求,仅需可下推的过滤条件[^4] | | **作用范围** | 减少扫描的分区数量 | 减少单分区内数据量(如过滤行、列裁剪) | --- #### 二、工作原理对比 ##### 1. 动态分区裁剪(DPP) - **触发条件**:维度表过滤后数据量远小于事实表,且关联键是事实表的分区键[^1]。 - **实现步骤**: 1. 执行维度表过滤,提取有效分区列表(如`date IN (2023-01-01, 2023-01-02)`); 2. 将分区列表注入事实表扫描操作,跳过无关分区; 3. 减少IO和计算资源消耗。 - **示例**: ```sql SELECT * FROM fact_table f JOIN dimension_table d ON f.date = d.date WHERE d.date BETWEEN &#39;2023-01-01&#39; AND &#39;2023-01-05&#39; ``` 通过DPP仅扫描`fact_table`中对应5天的分区,而非全表。 ##### 2. 谓词下推 - **触发条件**:过滤条件可下推到数据源(如Hive表、Parquet文件)[^3]。 - **实现步骤**: 1. 在逻辑计划中将过滤条件尽量靠近数据源; 2. 数据读取时跳过不满足条件的行或列(如Parquet文件的行组过滤); 3. 减少后续操作(如JOIN、聚合)的数据量。 - **示例**: ```sql SELECT * FROM table_a a JOIN table_b b ON a.id = b.id WHERE a.age > 20 AND b.salary < 10000 ``` 优化后,`a.age > 20`和`b.salary < 10000`会在扫描`table_a`和`table_b`时立即生效。 --- #### 三、技术互补性 1. **联合优化场景**:在星型模型查询中,DPP先减少事实表扫描的分区数,谓词下推再对每个分区内的数据进行过滤。 2. **性能增益叠加**:例如,分区剪裁减少90%数据量,谓词下推进一步减少50%,总体减少95%数据量。 --- #### 四、典型应用对比 | **技术** | **适用场景** | **优化效果** | |---------------|---------------------------------------------|--------------------------| | DPP | 大表关联小表且分区键匹配的查询(如时间范围过滤) | 减少分区扫描和Shuffle数据量 | | 谓词下推 | 所有含过滤条件的查询(如WHERE子句、JOIN条件) | 减少行/列处理量 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值