1. Intro

在此之前的分布式计算框架,例如 MapReduce,缺乏对分布式内存的复用能力。在 MapReduce 中,每一轮 Map 和 Reduce 任务之间的中间结果都会被写入磁盘,随后再从磁盘中读取用于下一阶段的计算。这种设计带来了大量的磁盘 I/O、数据复制和对象序列化/反序列化的开销,从而在许多迭代式或交互式的大数据应用中成为性能瓶颈,占据了绝大部分的执行时间。

为了解决上述问题,Spark 引入了弹性分布式数据集(Resilient Distributed Dataset,RDD)的抽象。RDD 是一个只读的分布式对象集合,可以并行操作,支持在内存中缓存中间计算结果,从而显著减少了 I/O 和序列化成本。RDD 提供了丰富的转换操作符(如 mapfilterreduceByKeycollect 等),使得开发人员能够以函数式编程方式,在大规模集群上构建高效、容错的内存计算任务。与此同时,RDD 通过血缘(lineage)信息来实现容错机制,即使节点失败也能基于操作链自动恢复数据,无需数据复制带来的额外存储成本。

2. RDD模型设计

RDD是一个只读,可分区、支持并行计算的数据集,可以通过操作符将RDD转换成新的RDD,并且两个RDD之间存在血缘关联。

RDD lineage

RDD的弹性体现在:

  • 存储:内存不足时可以和磁盘进行数据交换
  • 计算:计算出错时支持重试
  • 容错:数据丢失可以自动恢复
  • 分区:支持重新分区

下面是一个从HDFS读取文本文件,并执行计算的案例:

lines = spark.textFile("hdfs://...")  
errors = lines.filter(_.startsWith("ERROR"))  
errors.persist()
// Count errors mentioning MySQL:  
errors.filter(_.contains("MySQL")).count()  
// Return the time fields of errors mentioning  
// HDFS as an array (assuming time is field  
// number 3 in a tab-separated format):  
errors.filter(_.contains("HDFS"))  
      .map(_.split(\t)(3))  
      .collect()

下图是上述案例中RDD血缘关系图,不同的RDD之间通过转换操作符进行连接。

sample

2.1. Spark编程接口

Spark提供了以下转换操作:

transformations and actions

在对RDD进行转换时,会产生血缘依赖关系,不同的转换操作可能产生不同的依赖关系,例如map、filter、union等操作会产生窄依赖关系,join、group by操作会导致宽依赖关系

image.png

上述提到的宽窄关系,主要影响以下几个方面:

影响方面 窄依赖 宽依赖
调度 可以进行 流水线调度 需要 shuffle 重分区和 barrier
容错恢复 只需重算一个 partition 可能需要重算整个父 RDD
性能开销 无需 shuffle,性能开销小 涉及 shuffle,可能产生大量 I/O 和网络传输
并行度 高:partition 可以独立运行 受限:存在阶段边界(stage boundaries)
任务划分(stage) 可在同一 stage 执行 通常会产生新的 stage(触发 job 切分)

3. Spark实现

初版Spark使用约1.4万行Scala代码实现,使用Memos进行调度,并支持接入Hadoop,每个Spark任务都拥有自己的driver和worker节点。

3.1. 作业调度

Spark作业调度机制具备故障恢复能力,会根据RDD的血缘关系,来构建该任务需要执行的stage的DAG,要求每个阶段尽量包含更多的窄依赖操作,stage的边界是宽依赖所需执行的shuffle操作,若执行阶段任一Stage执行失败,只要父类RDD仍然可用,就可以重新调度运行。

image.png

3.2. 内存管理

Spark提供了三种持久化RDD的方案:Java对象的内存存储、序列化后的内存存储、硬盘存储,使用LRU策略来管理内存。Spark集群中每个任务都有独立的内存空间,互不影响。

3.3. Checkpoint

任务调度中提到,可以通过RDD血缘关系进行故障恢复,但是对于血缘关系过长的RDD来说,耗时过长,可以将RDD checkpoint到持久化存储中能加速故障恢复的过程。

4. 总结

本论文提出了 Resilient Distributed Datasets(RDD)这一核心抽象,用于构建容错且高效的分布式内存计算系统。RDD 是一个只读的分布式对象集合,支持以函数式方式进行并行操作,具备基于血缘关系(lineage)的容错机制。通过在内存中缓存中间结果,RDD 有效减少了迭代式和交互式计算中频繁的 I/O 与序列化开销。相较于传统如 MapReduce 等模型,RDD 在性能与可编程性方面均有显著提升。该论文的思想构成了 Apache Spark 的核心基础,极大推动了大数据计算框架的发展,使得 Spark 成为支持机器学习、图计算、大数据处理等场景的主流平台之一。