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

What are the differences between Dataframe, Dataset, and RDD in Apache Spark?

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

首先,让我们定义一下 spark 的作用

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

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

  • 最后,spark 尝试将数据保存在内存中以供处理,但它会 [ser/deser]在每个 worker 本地化数据,当它不适合内存时。同样,它是透明完成的,但成本可能很高。

APIs

  • RDD

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

  • 数据框

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

  • 数据集

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

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

优缺点

  • 数据集:

    • 优点:优化了面向列存储的操作
    • 优点:还有很多操作不需要反序列化
    • 优点:如果您喜欢,请提供 table/sql 语义(我不喜欢;)
    • 优点:数据集操作带有一个优化引擎“催化剂”,可以提高代码的性能(我不确定它是否真的那么好。如果你知道你的代码,意味着对数据,你的代码应该自己优化)
    • 缺点:大部分操作松散打字
    • 缺点:对于不适合它的复杂算法,数据集操作可能变得过于复杂。我知道的 2 个主要限制是管理无效数据和复杂的数学算法。
  • 数据框:

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

    • 优点:(真正)强类型
    • 优点:scala/java语义。您可以像处理内存中集合的标准应用程序一样设计您的代码。好吧,有了功能语义:)
    • 缺点:需要完整的 jvm 反序列化来处理数据,在前面提到的任何步骤:读取输入之后,以及需要数据在 worker 之间移动或存储在本地以管理内存绑定的所有处理步骤之间。

结论

  • 默认使用Dataset:

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

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

简而言之:

  • RDD 来自 Spark 的早期版本。 Dataframes 仍然在“幕后”使用。
  • Dataframes 在 Spark 1.x 后期引入,在 Spark 2.x 中真正成熟。它们现在是首选存储。它们在 Java.
  • 中作为数据集实现
  • 数据集 是通用实现,例如您可以有一个数据集。

我使用数据帧并强烈推荐它们:Spark 的优化器 Catalyst 理解更好的数据集(因此,数据帧)并且 Row 是比纯 JVM 对象更好的存储容器。您会在内部找到很多博客文章(包括 Databricks 的)。