RDD是Spark中的核心抽象元素,其正式名称为弹性分布式数据集(Resillient Distributed Dataset)。RDD是一个抽象的数据集合,一般是分区的,即分布式地存储在集群中的不同节点上,因此可以并行处理。一般来说,创建RDD的数据源通常来自Hadoop中的HDFS或HIVE中的文件或数据集,也可以基于应用程序内存中的集合来创建。关于RDD的重要特性之一是其容错性,即节点可以自动从处理失败中恢复,比如某个节点上的RDD分区由于节点故障处理失败,导致数据丢失,RDD会通过回溯数据来源并重新计算该分区上的作业,而对于调用方而言,这一过程是透明的。此外,RDD分区中的数据默认存储在内存中,但当内存资源不足时,会自动存储到磁盘中,这也是其弹性特性的表现之一。

下面来分析RDD分区结构。每个RDD数据集在逻辑上表示HDFS文件,实际上则以分区形式分布式存储在整个集群中,如下图所示,假设有RDD数据集对应了900万条记录,总共分为9个分区;其每个分区上存储100万条数据,默认存储在内存中。但假设内存占用率较高,无法完全容纳100万条的数据,此时分区中的部分数据会落入磁盘中保存(假设50万条数据落入磁盘)。对于使用方来说这个自动权衡和切换的机制是透明的。此外,如果某个节点发生故障,比如节点8临时故障导致数据处理失败,此时RDD会根据数据来源做回溯和重新计算,重新生成数据,同样对于调用方而言是透明无感知的。 图1:RDD分区概念图

接着再来看看Spark运行任务的基本流程。一般编写完Spark程序后,程序包会提交到Spark集群上运行。而Spark与传统的MapReduce技术的差异在于其计算模型是迭代式的,而非传统的两阶段模型。以传统的MapReduce为例,其处理过程分为两个典型阶段:map和reduce;可以认为这两个阶段处理完成则整个流程就完成了。而Spark的计算模型则可以进一步扩展到多个阶段(理论上可以分为N个阶段)。由于它是基于内存做迭代式计算,处理完某个阶段计算任务后,可以继续处理后续N个阶段。因此相比较两者计算模型,可以发现Spark提供的扩展性更强。

下面是Spark执行任务的基本流程图。可以看到每个节点上的每个分区数据,都是RDD集合的一部分。这些RDD数据集是需要初始化定义的,定义RDD数据来源,是从HDFS、HIVE或本地内存读取。每个节点上除了RDD数据,还包括计算操作(一般称为算子),比如常见的flatMap等计算逻辑。当第一阶段的节点计算完毕后,计算结果会发送到后续节点上,作为新的RDD集合执行下一轮计算,循环往复得到最终结果。因此Spark开发实际上就是定义RDD数据源并编写批处理计算逻辑(算子),将程序包发布到集群后在底层基于RDD和算子执行运算。 图2:Spark执行流程概述