为什么Apache Spark速度很快以及如何使其运行更快

如果在开发和实施Spark作业时应用所有这些规则,那么破纪录的处理工具将以惊人的结果回报您。

这些建议仅仅是掌握Apache Spark的第一步。 在接下来的文章中,我们将详细讨论其不同模块,以更好地了解Spark的功能。

本文最初在INVIVOO的技术博客上发表。


为什么Apache Spark速度很快以及如何使其运行更快

> Apache Spark logo

Spark可以快速完成任务。 自从2010年首次引入该框架以来,它一直是该框架的主要卖点。

提供基于内存的替代Map-Reduce极大地推动了大数据生态系统,并且在过去几年中,这代表了公司采用大数据系统的主要原因之一。

凭借其广泛的用例,易用性和记录设置功能,当涉及大数据架构中的数据处理时,Spark迅速成为每个人的首选框架。

第一部分:Spark ABC

Spark的关键组件之一是它的SparkSQL模块,它可以将批处理Spark作业编写为类似SQL的查询。 为此,Spark依靠复杂的机制在后台运行,以通过执行引擎运行这些查询。 该机制的核心是Catalyst:Spark的查询优化器,它通过生成作业的物理执行计划来完成许多繁重的工作。

即使此过程的每一步都经过精心改进,以优化工作的各个方面。 在链的末端,您仍然可以做很多事情,以使您的Spark作业运行得更快。 但是,在此之前,让我们深入探讨Catalyst的工作方式。

首先,让我们从基础开始

Spark提供了多种与其SparkSQL接口进行交互的方式,主要的API是DataSet和DataFrame。 这些高级API建立在面向对象的RDD API之上。 并且在保留某些主要功能(例如使用模式)的同时保留了其主要特征。 (有关详细比较,请参阅Databricks博客上的本文)。

使用的API的选择主要取决于您使用的语言。 DataSet仅在Scala / Java中可用,并且自Spark 2.0发行以来,已为这些语言替换DataFrame。 每个人都有一定的特权和优势。 好消息是,Spark在后台使用了相同的执行引擎来运行您的计算,因此您可以轻松地从一个API切换到另一个API,而不必担心执行级别上会发生什么。

也就是说,无论您使用哪种API,提交作业时都会经过统一的优化过程。

Spark如何看待世界

您可以在Spark应用程序中执行的操作分为两种类型:

· Transform 转换:这些操作在应用于RDD时,将返回对通过转换创建的新RDD的引用。 一些最常用的转换是过滤器和映射。 (以下是可用转换的完整列表)

· Action 行动:当应用于RDD时,这些操作将返回非RDD值。 一个很好的例子是count操作,该操作将RDD中的元素数返回给Spark驱动程序,或者收集一个将RDD的内容发送给驱动程序的操作。 (请参阅此链接以获取可在RDD上执行的操作的完整列表)。

由于这些API基于RDD机制构建,因此DataFrame和DataSet操作分为相同的类别。

接下来要做的区分是以下两种类型的转换:

· 窄转换:将这些转换应用于RDD时,分区之间没有数据移动。 将该转换应用于RDD的每个分区的数据,并得到具有相同分区数的新RDD,如下图所示。 例如,过滤器是一个狭窄的转换,因为将过滤器应用于每个分区的数据,并且所得数据表示新创建的RDD中的一个分区。

为什么Apache Spark速度很快以及如何使其运行更快

> A narrow transformation (Source: Databricks)

· 广泛的转换:这些转换需要在分区之间进行数据移动,也就是所谓的随机播放。 数据跨网络移动,新创建的RDD的分区基于多个输入分区的数据,如下所示。 一个很好的例子就是sortBy操作,其中所有输入分区中的数据都在生成带有新分区的RDD的过程中根据特定列进行排序。

为什么Apache Spark速度很快以及如何使其运行更快

> A wide transformation (Source: Databricks)

因此,当您将作业提交给Spark时,您提交的基本上是一系列操作和转换,然后由Catalyst转换为作业的逻辑计划,然后生成理想的物理计划。

第二部分:Spark 魔术

现在,我们知道了Spark如何看待提交给它的工作,让我们研究一下将动作和转换列表转换为工作的物理执行计划的机制。

Spark是个懒惰的魔术师

首先,使用Spark时要记住的一个重要概念是它依赖于惰性评估。 这意味着,当您提交作业时,Spark只会在必须执行时(即,当它收到一个动作时(例如,当驱动程序要求一些数据或何时需要将数据存储到HDFS中))发挥其魔力。

Spark无需立即一一运行转换,而是将这些转换存储在DAG(有向无环图)中,并且一旦接收到动作,它就会运行整个DAG并交付请求的输出。 这样一来,它就可以基于作业的DAG优化其执行计划,而无需顺序运行转换。

一切如何发生

Spark依靠其优化器Catalyst进行必要的优化,以生成最有效的执行计划。 Catalyst的核心包括一个通用库,专用于表示树并应用规则来操纵它们。 它利用Scala中的函数式编程构造,并提供特定于关系查询处理的库。

Catalyst的主要数据类型是由节点对象组成的树,该树上应用了一组规则对其进行优化。 这些优化通过四个不同的阶段执行,如下图所示:

为什么Apache Spark速度很快以及如何使其运行更快

> Catalyst's optimization phases (source: Databricks)

逻辑/物理计划

一开始可能不是很清楚的区别是术语"逻辑计划"和"物理计划"的使用。 简而言之,逻辑计划由一棵树组成,该树描述了需要做的事情,而没有暗示如何做,而物理计划则恰好描述了树中每个节点将要做什么。

例如,逻辑计划仅表示需要执行联接操作,而物理计划则为该特定操作修复了联接类型(例如ShuffleHashJoin)。

现在,我们来完成这四个步骤,并深入研究Catalyst的逻辑。

步骤1:分析

Catalyst优化管道的起点是一组未解决的属性引用或关系。 无论您使用的是SQL还是DataFrame / Dataset API,SparkSQL最初都不会对您的数据类型或您所指的列是否存在(这就是未解决的意思)一无所知。 如果您提交选择查询,SparkSQL将首先使用Catalyst来确定您传递的每一列的类型以及您所使用的列是否实际存在。 为此,它主要依赖于Catalyst的树和规则机制。

它首先为未解决的逻辑计划创建一棵树,然后开始在其上应用规则,直到解析所有属性引用和关系。 在整个过程中,Catalyst依赖于Catalog对象,该对象跟踪所有数据源中的表。

步骤2:逻辑优化

在此阶段,Catalyst获得了一些帮助。 随着2017年Spark 2.2的发布,引入了基于成本的优化器框架。 与基于规则的优化相反,基于成本的优化器使用统计信息和基数来查找最有效的执行计划,而不是简单地应用一组规则。

分析步骤的输出是一个逻辑计划,然后在第二步中进行一系列基于规则和基于成本的优化。 Catalyst将所有优化规则应用于逻辑计划,并与基于成本的优化器一起使用,以将优化的逻辑计划交付至下一步。

步骤3:物理规划

就像上一步一样,SparkSQL将Catalyst和基于成本的优化器同时用于物理规划。 在利用一组物理规则和统计信息提供最有效的物理计划之前,它会基于优化的逻辑计划生成多个物理计划。

步骤4:代码生成

最后,Catalyst使用准符号(Scala提供的特殊功能)来生成要在每台计算机上运行的Java字节码。 Catalyst通过将作业的树转换为由Scala评估的抽象语法树(AST)来使用此功能,然后由该树编译并运行生成的代码。

总结一下

Spark SQL依靠复杂的管道来优化其需要执行的作业,并且在此过程的所有步骤中都使用其优化器Catalyst。 这种优化机制是Spark天文性能及其有效性的主要原因之一。

第三部分:将Spark推向新的高度

现在,我们检查了Spark的复杂优化过程,很明显,Spark依靠精心打造的机制来达到惊人的速度。 但是认为无论您如何做,Spark都能为您带来最佳效果,这是一个错误。

尤其是从另一个数据处理工具迁移时,很容易做出假设。 与您一直使用的工具相比,处理时间减少了50%,这可能使您相信Spark正在全速运行,并且您无法进一步减少执行时间。 事实是,可以。

通过上面讨论的过程,Spark SQL及其优化程序Catalyst可以自行完成奇迹,但是通过一些曲折和技巧,您可以将Spark提升到一个新的水平。 因此,让我们讨论如何从频谱的末端优化Spark作业

总是在引擎盖下看一看

使用Spark时要记住的第一件事是,执行时间本身并没有多大的意义。 要评估作业的绩效,重要的是要知道幕后运行情况。 在开发和测试阶段,您需要经常使用explain函数来查看要分析的语句生成的物理计划,而对于深入分析,您可以添加扩展标志以查看Spark的不同计划。 选择了SQL(从解析的逻辑计划到物理计划)。 这是检测潜在问题和不必要阶段的好方法,而无需实际执行作业。

知道何时使用缓存

处理大型数据集和复杂作业时,缓存非常重要。 它允许您保存计划在后续阶段中使用的数据集,以使Spark不再从头开始创建它们。 这种优势有时使开发人员陷入"超高速缓存"状态,从而使高速缓存的数据集成为负担,使您的工作变慢而不是对其进行优化。 要确定需要缓存的数据集,您必须准备整个工作,然后通过测试尝试找出哪些数据集真正值得缓存,以及在什么时候可以不持久化以释放它们在内存中占据的空间。 已缓存。 有效地使用缓存可以使Spark更快地运行某些计算10倍,这可以大大减少作业的总执行时间。

了解您的集群和数据

充分利用Spark的关键要素是根据您的集群微调其配置。 在某些情况下,可能要依靠默认配置,但是通常您离获得更令人印象深刻的结果仅是一个参数。 选择合适的执行程序数量,每个执行程序的内核数量以及每个执行程序的内存大小都是可以极大地影响您的工作性能的要素,因此请毫不犹豫地执行基准测试以查看是否可以使用某些参数。 优化。

最后,要牢记的一个重要因素是,您需要知道要处理的数据以及每次操作的期望。 如果一个阶段花费的时间过长,即使它处理的数据少于其他阶段,那么您应该检查另一侧正在发生的事情。 在执行繁重的代码和运行代码时,Spark很棒,但是只有您可以检测到与您定义工作方式有关的业务相关问题。

(本文翻译自Mahdi Karabiben的文章《Why Apache Spark Is Fast and How to Make It Run Faster》,参考:https://towardsdatascience.com/why-apache-spark-is-fast-and-how-to-make-it-run-faster-9d31bf3eae04)


分享到:


相關文章: