简书 Wwwwei
转载请注明原创出处,谢谢!
前言
Spark是专为大规模数据处理而设计的快速通用的计算引擎,随着大数据的兴起,应用场景越来越丰富。而RDD作为一种新型的内存抽象模型,是Spark核心功能的基础,理解RDD的思想和实现不仅仅有助于了解和使用Spark计算引擎,更有助于应用其设计思想解决其他大数据问题。
同之前系列一样,本文不会过多的讲如何使用RDD,更注重说清楚为什么需要RDD、什么是RDD以及如何实现RDD。
为什么需要RDD
传统分布式计算框架的局限性
分布式计算框架是针对大数据应用场景的计算框架,以分布式的形式把巨大的计算任务分成小的单机可以承受的计算任务,解决常规单机计算模式无法支撑巨大数据量的问题。
大多数传统的分布式计算框架都是基于非循环的数据流模型,即从稳定的物理存储(如分布式文件系统)中加载记录,在执行计算任务时,当一组确定性操作完成后,通常将中间结果写回磁盘中稳定存储,每次查询时再重新加载。
尽管非循环数据流是一种很强大的抽象方法,但仍然有些应用无法使用这种方式描述,例如机器学习和图应用中常用的迭代算法(每一步对数据执行相似的函数)和交互式数据挖掘工具(用户反复查询一个数据子集),这类应用需要在多个并行操作之间重用工作数据集,即中间结果。
因此,基于数据流的架构并不明确支持工作集。因为每次查询中间结果会产生大量的I/O消耗,极大程度的影响了计算的性能和效率。
基于内存的分布式计算构想
针对这个问题,需要提供一种新的分布式计算构想,它要求既能够保持传统分布式计算框架,例如MapReduce及其相关模型的优势特性,即自动容错、位置感知性调度和可伸缩性,同时够对基于工作集的计算任务也具有良好的描述能力,即支持中间结果的复用场景。
因此,在传统的分布式计算框架基础上,开发人员希望将计算的中间结果存储由磁盘转为内存,提供一种在大型集群上执行基于内存的计算构想,消除磁盘中加载中间结果带来的I/O损耗,最大程度的提高计算性能。
Apache Spark由此诞生,作为专为大规模数据处理而设计的快速通用的计算引擎,拥有Hadoop MapReduce所具有的优点;但不同于MapReduce的是——Job中间输出结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的MapReduce的算法。
什么是RDD
RDD的提出
为了满足Spark基于内存的分布式计算思想,需要定义一种分布式内存抽象,保证在分布式环境中能够正确、可靠、高效地完成计算任务。
如何定义这种分布式内存抽象,需要考虑多方面的因素。首先,分布式内存抽象需要具有传统分布式计算框架的优点,即自动容错、位置感知性调度和可伸缩性。其次,为了提高迭代计算的性能和分布式并行计算下共享数据的容错性,在设计上还需要符合基于内存的分布式计算的实际需求,例如将数据集分区存储在节点的内存中,减少迭代过程(如机器学习算法)反复的I/O操作从而提高性能以及数据集不可变,并记录其转换过程,从而实现无共享数据读写同步问题、以及出错的可重算性。
基于以上原则,伯克利的设计者提出了RDD,即弹性分布式数据集的概念。
RDD的定义
RDD是一种分布式的内存抽象,称为弹性分布式数据集(Resilient Distributed Datasets),是Apache Spark中数据的核心抽象,是一种只读的、分区的数据记录集合。RDD支持基于工作集的应用,同时具有数据流模型的特点,即自动容错、位置感知性调度和可伸缩性,允许用户在执行多个查询时显式地将工作集缓存在内存中,极大地加速了后期的工作集重用。
怎样实现RDD
RDD的逻辑物理结构
RDD是一种内存抽象,可以认为是一个分布式的数组,数组的元素是RDD的分区(Partition),分布在集群上。在物理数据存储上,RDD的每一个分区对应的就是一个数据块(Block),数据块可以存储在内存中,当内存不够时可以存储在磁盘上。RDD的逻辑物理结构如下图所示:
RDD的数据存储结构
每个RDD的数据都以数据块(Block)的形式存储于多台机器上,下图是Spark的RDD存储架构原理图:
其中每个Executor会启动一个BlockManagerSlave,并管理一部分Block;而Block的元数据由Driver节点的BlockManagerMaster保存。BlockManagerSlave生成Block后向BlockManagerMaster注册该Block,BlockManagerMaster管理RDD与Block的关系,当RDD不再需要存储的时候,将向BlockManagerSlave发送指令删除相应的Block。
RDD是Spark的计算单元
RDD是Spark的计算单元,算子是RDD中定义的函数,可以对RDD中的数据进行转换和操作。
RDD有两种操作算子:转换(Transformation)与行动(Action),其中转换操作是从一个RDD转换创建一个新的RDD,这种操作是延迟计算的,也就是说不是马上执行,需要等到有Action操作的时候才会真正触发运算;行动操作对RDD操作后把结果返回给Driver,会触发Spark提交作业(Job),并将数据输出Spark系统。下图展示了RDD算子的执行过程:
Spark从外部空间(HDFS)读取数据形成RDD_0,Transformation算子对数据进行操作(如filter)并转化为新的RDD_1、RDD_2,通过Action算子(如collect/count)触发Spark提交作业。
如上的分析过程可以看出,Transformation算子并不会触发Spark提交作业,直至Action算子才提交作业,这是一个延迟计算的设计技巧,可以避免内存过快被中间计算占满,从而提高内存的利用率。
RDD是Spark的运行逻辑的载体
Spark采用了分布式计算中的Master-Slave模型。Master作为整个集群的控制器,负责整个集群的正常运行;Worker是计算节点,接受主节点命令以及进行状态汇报;Executor是Worker节点行的一个进程,负责任务(Task)的调度和执行;Client作为用户的客户端负责提交应用;Driver同样也是Worker节点的一个进程,负责控制一个应用的执行。Spark架构如下图所示:
从Spark的架构角度来看,RDD是Spark的运行逻辑的载体。一个Spark应用的执行过程可以分为五个步骤,如下图所示:
(1)Spark集群启动时,需要从主节点和从节点分别启动Master进程和Worker进程,对整个集群进行控制。
(2)启动Driver进程。在一个Spark应用的执行过程中,Driver是应用的逻辑执行起点,运行应用的main函数并创建SparkContext。
(3)构建RDD的有向无环图DAG(Directed Acyclic Graph)。在Spark应用的执行流程中,逻辑运算会使用许多转换操作,而每个转换操作都会生成新的RDD,使得新的RDD和原有的RDD之间产生了依赖关系,行动操作触发之后会将所有累积的转换操作产生的RDD之间的依赖关系形成一个有向无环图DAG。RDD的有向无环图DAG,记录了RDD的更新过程,当这个RDD的部分分区数据丢失时,它可以通过DAG获取足够的信息来重新运算和恢复丢失的数据分区。
(4)根据RDD的有向无环图DAG划分阶段(Stage)。SparkContext中的DAGScheduler把应用程序中每个Job中的RDD的有向无环图根据依赖关系划分为多个阶段,一个阶段包含一系列函数进行流水线执行。RDD之间的依赖关系分为两种,分别是窄依赖(Narrow Dependency)与宽依赖(Wide Dependency),其中Wide Dependency为子RDD的每个Partition都依赖于父RDD的所有Partition,而Narrow Dependency则只依赖一个或部分的Partition。下图说明了窄依赖与宽依赖之间的区别:
下图是一个根据RDD的有向无环图DAG划分阶段(Stage)的示例。图中的A、B、C、D、E、F、G,分别代表不同的RDD,其中RDD内的一个方框代表一个数据块。数据从HDFS输入Spark,形成RDD A和RDD C,RDD C上执行map操作,转换为RDD D,RDD B和RDD F进行join操作转换为G,而在B到G的过程中又会进行Shuffle。最后RDD G通过函数saveAsSequenceFile输出保存到HDFS中。
(5)根据阶段(Stage)划分任务(Task),调度任务进行运算。每一个Stage是一个任务集合(Task Set),TaskScheduler把Task分发给Worker中的Executor,Worker启动Executor,Executor启动线程池用于执行Task。
总结
由于数据爆炸性增长和大数据需求日益增加,传统分布式计算框架存在局限性,不能够很好的应用于需要反复复用中间结果的非循环模型应用。基于内存的分布式计算框架Apache Spark因此诞生,作为Spark的核心和架构基础,RDD(Resilient Distributed Datasets),即弹性分布式数据集,在设计上结合了传统分布式计算框架的优点,即自动容错、位置感知性调度和可伸缩性,以及为了符合基于内存的分布式计算的实际需求,提高迭代计算的性能和分布式并行计算下共享数据的容错性,应用了有向无环图DAG记录RDD的依赖关系、高度受限的共享内存机制、延时加载等理念。
最后,特别感谢以下论文和博文,写作时作为参考借鉴。
[1] Zaharia M, Chowdhury M, Franklin M J, et al. Spark: cluster computing with working sets[C]// Usenix Conference on Hot Topics in Cloud Computing. USENIX Association, 2010:10-10.
[2] Zaharia M, Chowdhury M, Das T, et al. Resilient distributed datasets: a fault-tolerant abstraction for in-memory cluster computing[C]// Usenix Conference on Networked Systems Design and Implementation. USENIX Association, 2012:2-2.
[3] spark RDD的原理
[4] 理解Spark的核心RDD
简书 Wwwwei
转载请注明原创出处,谢谢!