【问题标题】:dynamic partition pruning not clear动态分区修剪不清楚
【发布时间】:2019-10-16 11:23:40
【问题描述】:

我正在尝试了解 spark 3 中的新功能:动态分区修剪。

看看这个测试:

https://github.com/apache/spark/blob/master/sql/core/src/test/scala/org/apache/spark/sql/DynamicPartitionPruningSuite.scala#L257

我不明白为什么它是动态的和经典的修剪?

谢谢

【问题讨论】:

    标签: apache-spark apache-spark-sql


    【解决方案1】:

    Spark 3.0 中的动态分区修剪

    随着 Spark 3.0 的发布,实施了重大改进,以使 Spark 能够更快地执行,并且随之而来的是许多新功能。其中,动态分区剪枝就是其中之一。在深入了解动态分区修剪中的新功能之前,让我们先了解一下什么是分区修剪。

    Spark 中的分区修剪

    在标准数据库中,修剪意味着优化器将避免读取不能包含您正在查找的数据的文件。例如,

    select * from Students where subject = ‘English’;
    

    在这个简单的查询中,我们试图匹配和识别学生表中属于主题英语的记录。这转化为一种简单的形式,即扫描之上的过滤器,这意味着首先扫描整个数据,然后根据条件过滤掉。

    现在大多数查询优化器都尝试将过滤器从扫描顶部向下推到尽可能靠近数据源的位置,以避免扫描整个数据集。

    在分区剪枝技术中,它遵循过滤器下推方法,对数据集进行分区。因为在这种情况下,如果您的查询在分区列上有一个过滤器,您实际上可以跳过完整的分区文件集。

    Spark 中的分区修剪是一种性能优化,它限制 Spark 在查询时读取的文件和分区的数量。对数据进行分区后,匹配特定分区过滤条件的查询通过允许 Spark 仅读取目录和文件的子集来提高性能。当存在分区过滤器时,催化剂优化器会向下推分区过滤器。扫描只读取与分区过滤器匹配的目录,从而减少磁盘 I/O。

    然而,实际上数据工程师并不仅仅在他们的查询中执行单个查询或单个过滤器,而且常见的情况是他们实际上有维度表,即需要与更大的事实表连接的小表。所以在这种情况下,我们不能再应用静态分区修剪,因为过滤器位于连接的一侧,而更吸引人且对修剪更有吸引力的表位于连接的另一侧。所以我们现在有一个问题。

    select * from Students join DailyRoutine
    where DailyRoutine.subject = ‘English’;
    

    有些人可能会建议我们可以预先将维度表与事实表连接起来。通过这种方式,我们仍然可以在单个表上触发静态修剪。然后他们可以在单独的查询中执行过滤器,如下所示。

    这种方法有明显的缺点,因为首先我们必须执行这个非常昂贵的连接。我们正在复制数据,因为我们必须生成另一个中间表。这张桌子可能很宽,因为我们需要一堆较小的桌子,然后将它们与一张大桌子连接在一起。而且它不仅很宽,而且面对更新维表实际上是非常难以管理的。因此,每当我们进行更改时,我们实际上必须重新触发整个管道。

    在这篇博客中,我们将学习一种完全不同的方法,我们将在其中使用动态修剪进行过滤。这种优化的基本目标是能够从维度表中获取过滤结果。然后直接使用它们来限制我们将从事实表中获取的数据。

    Spark 中的动态分区修剪

    在 Spark SQL 中,用户通常使用自己喜欢的编程语言从自己喜欢的 API 提交查询,因此我们拥有数据框和数据集。 Spark 接受此查询并将其转换为可消化的形式,我们称之为查询的逻辑计划。在此阶段,Spark 通过应用一组转换来优化逻辑计划,这些转换是基于规则的转换,例如列修剪、常量折叠、过滤器下推。只有稍后,它才会进入查询的实际物理规划。在物理计划阶段,spark 会生成一个可执行的计划。该计划将计算分布在许多机器的集群中。在这里,我将解释如何在逻辑规划级别实现动态分区修剪。然后我们将研究如何在物理规划期间进一步优化它。

    逻辑级优化

    让我们从逻辑规划层面的优化机会开始。让我们考虑一个跨多个文件分区的数据集。每个分区都会因特定的颜色而有所不同。在另一边,我们将有一个较小的表,它是一个不一定分区的维度表。然后我们在这些数据集之上拥有典型的扫描运算符。每当我们过滤维度表时,请考虑一个示例,其中只有对应于连接另一侧的两个分区的行实际上是相关的。所以当我们完成最后的join操作时,实际上只有这两个partition会被join保留。

    因此我们不需要实际扫描整个事实表,因为我们只对维度表产生的两个过滤分区感兴趣。为了避免这种情况,一种简单的方法是从合并到子查询中的维度表中获取过滤器。然后在事实表的扫描下方运行该子查询。

    通过这种方式,我们可以弄清楚,当我们计划连接的事实端时。我们能够弄清楚这个连接需要哪些数据。这是一种简单的方法。

    但它实际上可能很昂贵。我们需要摆脱这种子查询重复并找出一种更有效的方法。为此,我们将了解 spark 在物理规划期间如何执行连接,以及 spark 在此物理规划阶段如何转换查询。

    物理层面的优化

    如果维度表很小,那么 Spark 很可能会将连接作为广播哈希连接执行。每当我们有两个表是哈希连接时,就会发生很多事情-

    1. 首先,Spark 从维度表构建一个哈希表,我们称之为构建关系。
    2. 在它执行了这个构建关系之后,它会将那一方的结果插入到一个广播变量中。 Spark 将该变量分配给参与计算的所有工作人员。
    3. 通过这样做,我们能够执行连接,而无需随机播放。
    4. 然后 spark 将开始使用来自每个工作节点上的事实表的行来探测该哈希表。

    现在这两个阶段之间显然存在天然屏障。所以首先我们计算连接的广播端。我们正在分发它,直到稍后我们才开始探测和执行实际的连接。这很有趣,我们希望能够将其用于我们的优化。因为这正是我们用子查询的逻辑规划级别所模仿的。

    这就是我们实际要做的事情。我们正在拦截构建端的结果——广播结果。我们将直接使用它们并将它们作为动态过滤器插入到事实表顶部的扫描仪中。所以这实际上是一个非常有效和优化的动态分区修剪版本。

    总而言之,在 Apache sparks 3.0 中,实现了一种称为动态分区修剪的新优化,该优化适用于:

    1. 逻辑规划级别,用于查找维度过滤器并通过连接传播到扫描的另一侧。
    2. 将其连接在一起的物理级别,以使此过滤器仅在维度侧执行一次。
    3. 然后过滤器的结果在表的扫描中直接重用。通过这两种方法,我们可以显着提高 Spark 中许多查询的速度。

    我希望这个答案有帮助!

    【讨论】:

    • 开始追赶ORACLE、DB2等
    【解决方案2】:

    只是说明与传统数据库优化器相比有多大的赶超。

    如果您有一个表/存储,并且在 where / 过滤器上提供了文字,则分区修剪可与 Spark 一起使用,并且它知道在“解析”时对分区进行过滤。

    使用动态分区修剪,当优化器无法在“解析时”识别它必须消除的实际分区时,也会发生这种情况。 IE。在“运行时”,它可以决定要消除哪些分区。

    例如星型模式连接,维度过滤掉所需的数据(例如月份或月份),并且该事实表基于该实际过滤,即按月分区。

    【讨论】:

      猜你喜欢
      • 2012-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多