动态分区修剪不清晰

时间:2019-10-16 11:23:40

标签: apache-spark

我试图了解Spark 3中的新功能:动态分区修剪。

看这个测试:

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

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

谢谢

2 个答案:

答案 0 :(得分:5)

Spark 3.0中的动态分区修剪

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

Spark中的分区修剪

在标准数据库中,修剪意味着优化程序将避免读取无法包含您要查找的数据的文件。例如,

select * from Students where subject = ‘English’;

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

enter image description here

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

enter image description here

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

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

但是,实际上,数据工程师不仅执行单个查询或查询中的单个过滤器,而且常见的情况是他们实际上有维表,小表,需要将它们与较大的事实表结合在一起。因此,在这种情况下,我们将无法再应用静态分区修剪,因为过滤器位于联接的一侧,而对修剪更有吸引力且更具吸引力的表位于联接的另一侧。所以我们现在有一个问题。

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

enter image description here

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

enter image description here

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

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

Spark中的动态分区修剪

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

enter image description here

逻辑层次上的优化

让我们从逻辑规划级别的优化机会开始。让我们考虑一个跨多个文件分区的数据集。每个分区的颜色会有所不同。在相反的一侧,我们将有一个较小的表,该表是未必已分区的维度表。然后,在这些数据集之上我们具有典型的扫描运算符。每当我们过滤维度表时,请考虑一个示例,其中仅与联接相对侧的两个分区相对应的行实际上是相关的。因此,当我们完成最后的联接操作时,联接实际上只会保留这两个分区。

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

这样,我们可以弄清楚在计划联接的事实方面时。而且我们能够找出该联接需要哪些数据。这是一种简单的方法。

enter image description here

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

物理层面的优化

如果维度表较小,则spark可能会以广播哈希联接的形式执行该联接。每当我们有两个表为哈希联接时,就会发生很多事情-

  1. 首先,Spark在维度表之外构建了一个哈希表,我们称之为构建关系。
  2. 执行此生成关系后,它将把该端的结果插入广播变量中。 Spark将变量分配给计算中涉及的所有工作人员。
  3. 这样做,我们可以执行连接而无需进行重新排序。
  4. 然后spark将开始使用来自每个工作程序节点上的事实表的行来探测该哈希表。

enter image description here

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

这就是我们实际要做的。我们正在拦截构建端的结果-广播结果。我们将直接将它们作为事实表顶部的扫描器内部的动态过滤器进行插入。因此,这实际上是动态分区修剪的非常有效且经过优化的版本。

enter image description here

总而言之,在Apache sparks 3.0中,实现了一种称为动态分区修剪的新优化,该优化在以下两个位置均有效:

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

我希望这个答案有帮助!

答案 1 :(得分:1)

仅说明与传统的数据库优化器有多少联系。

如果您只有一个表/存储,并且在where /过滤器上提供了文字,那么分区清理将与Spark一起使用,并且它知道在“解析”时对分区的过滤。

通过动态分区修剪,当优化器无法在“解析时间”确定必须消除的实际分区时,也会发生这种情况。即在“运行时”,它可以决定要消除的分区。

例如星型模式联接,其维度可以过滤出所需的数据(例如,一个月或一个月),而事实表则基于该实际过滤条件,即也可以按月进行分区。

相关问题