Apache Spark 中的 Dataframe、Dataset 和 RDD 有什么区别?

Arj*_*M.A 2 java jvm scala hdfs apache-spark

在 Apache Spark 中,这些 API 之间有什么区别?为什么以及何时我们应该选择其中之一而不是其他?

Juh*_*uh_ 7

首先,我们来定义 Spark 的作用

  • 简单来说它的作用就是对分布式数据执行操作。因此,操作也需要分布式。有些操作很简单,例如过滤掉所有不遵守某些规则的项目。其他则更复杂,例如需要移动数据的 groupBy,以及需要关联来自 2 个或更多数据集的项目的 join。

  • 另一个重要的事实是输入和输出以不同的格式存储,spark 有连接器来读取和写入这些格式。但这意味着对它们进行序列化和反序列化。虽然序列化是透明的,但它通常是最昂贵的操作。

  • 最后,spark 尝试将数据保留在内存中进行处理,但当内存不适合时,它会在本地对每个工作线程上的数据进行[序列化/反序列化]。再次强调,它是透明完成的,但成本可能很高。有趣的事实:估计数据大小可能需要时间

API

  • RDD

这是spark提供的第一个API。简而言之,它是分布在集群上的无序 scala/java 对象序列。在其上执行的所有操作都是jvm方法(传递给map,flatmap,groupBy,...),需要序列化,发送给所有worker,并应用于那里的jvm对象。这与使用 scala Seq 几乎相同,但是是分布式的。它是强类型的,这意味着“如果它可以编译,那么它就可以工作”(如果你不作弊的话)。然而,可能会出现很多分配问题。特别是如果 Spark 不知道如何[反]序列化 jvm 类和方法。

  • 数据框

它出现在 RDD 之后,并且在语义上与 RDD 有很大不同。数据被视为表,可以对其应用诸如sql操作之类的操作。它根本没有键入,因此在执行过程中随时可能出现错误。但是,我认为有 2 个优点:(1)很多人习惯了 table/sql 语义和操作,(2)如果数据格式不同,spark 不需要反序列化整行来处理其列之一提供合适的色谱柱访问。许多格式都是这样做的,例如最常用的 parquet 文件格式。

  • 数据集

它是 Dataframe 的改进,带来了一些类型安全性。数据集是我们将与 jvm 类相关的“编码器”关联到的数据帧。因此spark可以在执行代码之前检查数据模式是否正确。但请注意,我们有时会读到数据集是强类型的,但事实并非如此:它带来了一些强类型安全性,您无法编译使用类型未声明的数据集的代码。但是很容易使代码可以编译但在运行时仍然失败。这是因为许多数据集操作都会丢失类型(除了过滤器之外几乎所有操作)。但这仍然是一个巨大的改进,因为即使我们犯了错误,它也会很快失败:失败发生在解释 Spark DAG 时(即开始时),而不是在数据处理期间。

注意:Dataframe 现在只是无类型数据集 ( Dataset<Row>)

注2:Dataset提供了RDD的主要API,如map、flatMap。据我所知,转换为rdd,然后应用map/flatMap,然后转换为dataset是一条捷径。它很实用,但也隐藏了转换,使得人们很难意识到可能发生了代价高昂的串行/反序列化。

优点和缺点

  • 数据集:

    • 优点:对面向列的存储进行了优化操作
    • 优点:许多操作也不需要反序列化
    • 优点:如果您喜欢的话,可以提供表/sql 语义(我不喜欢;)
    • 优点:数据集操作带有优化引擎“催化剂”,可以提高代码的性能。但我不确定它是否真的那么好。如果您知道您的代码是什么,即对数据做了什么,您的代码应该自行优化。
    • 缺点:大多数操作都是松散打字
    • 缺点:对于不适合它的复杂算法来说,数据集操作可能变得过于复杂。我知道的两个主要限制是管理无效数据和复杂的数学算法。
  • 数据框:

    • 优点:在丢失类型的数据集操作之间需要
    • 缺点:只需使用 Dataset 它就具有所有优点以及更多
  • RDD:

    • 优点:(确实)强类型
    • 优点:scala/java 语义。您可以像处理内存中集合的单 jvm 应用程序一样设计代码。嗯,具有功能语义:)
    • 缺点:在前面提到的任何步骤中都需要完整的 jvm 反序列化来处理数据:读取输入之后,以及需要在工作程序之间移动数据或本地存储以管理内存限制的所有处理步骤之间。

结论

  • 默认使用数据集:

    • 使用编码器读取输入,如果数据格式允许,它将在开始时验证输入模式
    • 使用数据集操作,当您松开类型时,返回到类型化的数据集。通常,使用类型化数据集作为所有方法的输入和输出。
  • 在某些情况下,您想要编码的内容过于复杂,无法使用数据集操作来表达。大多数应用程序不会,但它经常发生在我实现复杂数学模型的工作中。在这种情况下:

    • 从数据集开始
    • 使用数据集操作尽可能多地过滤和洗牌(groupBy、join)数据
    • 一旦你只有所需的数据,并且不需要移动它们,转换为 rdd 并应用复杂的计算。