SparkCore 之 RDD | 青训营笔记

82 阅读5分钟

image.png

这是我参与「第四届青训营 」笔记创作活动的的第8天
[第四届青训营笔记创作活动]


一、什么是 RDD

RDD(Resilent Distributed Datasets)弹性分布式数据集,是Spark底层的分布式存储的数据结构,可以说是 Spark 的核心, Spark API 的所有操作都是基于 RDD 的

二、RDD 的五大要素

1. Partitions

一组分片(A list of partitions),是数据集的基本组成单位。对于 RDD 来说每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建 RDD 时指定 RDD 的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的 CPU Core 的数目。

2. Compute

一个计算每个分片的函数(A function of computing each split)。Spark 中,RDD 的计算是以分片为单位的每个 RDD 都会实现 compute 函数以达到这个目的。compute 函数会对迭代器进行复合,不需要保存每次计算的结果。

3. Dependencies

RDD 之间的依赖关系(A list of dependencies on other RDDs)。RDD 的每次转换都会生成一个新的 RDD,所以 RDD 之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark 可以通过这个依赖关系重新计算丢失的分区数据,而不是对 RDD 的所有分区进行重新计算。

4. Partitioner

RDD 的分区器(a Partitioner for key-value RDDs)。当前 Spark 中实现了两种类型的分片函数,一个是基于哈希的 HashPartitioner,另外一个是基于范围的 RangePartitioner。只有对于于 key-value 的RDD , 才会有 Partitioner, 非 key-value 的RDD 的Parititioner 的值是 None。Partitioner 函数不但决定了 RDD 本身的分片数量, 也决定了 parent RDD Shuffle 输出时的分片数量。

5. PreferredLocations

每个分片的优先存储位置(a list of preferred locations to compute each split on)。对于一个 HDFS 文件来说,这个列表保存的就是每个 Partition 所在的块的位置。按照“移动数据不如移动计算”的理念,Spark 在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

三、RDD 的特点

RDD 表示只读的分区的数据集,若需要对 RDD 进行改动,只能通过 RDD 的 transform 算子得到一个新的 RDD。RDDs 之间存在依赖,对于 RDD 的执行是按照其血缘关系(Lineage) 的顺序进行计算的。也可以通过持久化 RDD 记录数据,切断血缘关系。

1. 分区

RDD 逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候会通过一个 compute函数得到每个分区的数据。如果 RDD 是通过已有的文件系统构建,则 compute 函数是读取指定文件系统中的数据,如果 RDD 是通过其他 RDD 转换而来,则 compute 函数是执行转换逻辑将其他 RDD 的数据进行转换。

2. 只读

RDD 是只读的,要想改变 RDD 中的数据,只能在现有的 RDD 基础上创建新的 RDD。

RDD 包含两类算子:transform 算子和 action 算子。通过 transform 算子进行 RDDs 之间的转化,形成血缘关系;通过 action 算子触发 RDD 的计算或者将 RDD 保存到文件系统中。

3. 依赖

RDDs 通过操作算子进行转换,转换得到的新 RDD,包含了从其他 RDDs 衍生所必需的信息,RDDs 之间维护着这种血缘关系,也称之为依赖。依赖包括两种,一种是窄依赖(NarrowDependency), RDDs 之间分区是一对一的;一种是宽依赖(ShuffleDependency), 下游 RDD 的每个分区与上游 RDD(也称之为父RDD)的每个分区都有关,是多对多的关系。简单来说,算子每进行一次 Shuffle 操作,就会产生一次宽依赖。

4. 缓存

如果在应用程序中多次使用同一个 RDD,可以将该 RDD 缓存起来,该 RDD 只有在第一次计算的时候会根据血缘关系得到分区的数据,在后续其他地方用到该 RDD 的时候,会直接从缓存处取而不用再根据血缘关系计算,可以加速对于 RDD 的重用。

5. 持久化(checkpoint)

当 RDD 的某个分区数据失败或丢失,可以通过血缘关系重建。但是对于长时间迭代型应用来说,随着迭代的进行,RDDs 之间的血缘关系会越来越长,一旦在后续迭代过程中出错,则需要通过非常长的血缘关系去重建,势必影响性能。为此,RDD 支持 checkpoint 将数据保存到持久化的存储中,这样就可以切断之前的血缘关系, 因为 checkpoint 后的 RDD 不需要知道它的父 RDDs 了, 它可以从 checkpoint 处直接读取数据。

四、总结

对于一个给定的 RDD 而言,我们至少可以知道以下几点信息:

  • 分区数以及分区方式
  • 由父RDDs衍生而来的相关依赖信息
  • 计算每个分区的数据,计算步骤为:
      1. 如果被缓存,则从缓存中取的分区的数据
      1. 如果被checkpoint,则从checkpoint 处恢复数
      1. 根据血缘关系计算分区的数据