本章要点:
- 什么是spark
- Spark生态圈
- RDD编程模型
1.1 什么是Spark
Apache Spark是一个由加州大学伯克利分校开发的一站式通用大数据框架,是围绕速度、易用性和复杂分析构建的大数据处理框架。Spark的核心技术是弹性分布式数据集(Resilient Distributes Dataset,RDD),提供了更加丰富的MapReduce模型,拥有Hadoop MapReduce的所有优点,但是与Hadoop和Storm等其他大数据和MapReduce技术相比,Spark还有如下优势:Spark提供了一个全面、统一的框架用于管理各种有着不同性质(文本数据、图表数据等)的数据集和数据源(批量数据或实时的流数据)的大数据处理的需求,还支持复杂的机器学习。
1.2 Spark 生态圈
Spark是基于RDD,提供了一站式多维度的大数据计算模型。可以在一个技术栈里快速对数据进行批处理、即席查询、机器学习、图计算和准实时流处理等。Spark整个生态系统与称为伯克利数据分析栈(RDAS)。其核心框架是Spark,同时BDAS涵盖支持数据结构化数据SQL查询与分析的查询引擎Spark SQL,提供具有机器学习功能的系统MLbase及底层的分布式机器学习库MLlib、并行图计算框架GraphX、流计算框架Spark Streaming、采样近似计算查询引擎BlinkDB、内存分布式文件系统Tachyon、资源管理框架Mesos等子项目。这些子项目在Spark上层提供了更高层、更丰富的计算范式。
Spark架构图如下:
- Spark Core:包含Spark的基本功能;尤其是定义RDD的API、操作以及这两者上的动作。其他Spark的库都是构建在RDD和Spark Core之上的
- Spark SQL:提供通过Apache Hive的SQL变体Hive查询语言(HiveQL)与Spark进行交互的API。每个数据库表被当做一个RDD,Spark SQL查询被转换为Spark操作。
- Spark Streaming:对实时数据流进行处理和控制。Spark Streaming允许程序能够像普通RDD一样处理实时数据
- MLlib:一个常用机器学习算法库,算法被实现为对RDD的Spark操作。这个库包含可扩展的学习算法,比如分类、回归等需要对大量数据集进行迭代的操作。
- GraphX:控制图、并行图操作和计算的一组算法和工具的集合。-GraphX扩展了RDD API,包含控制图、创建子图、访问路径上所有顶点的操作。
RDD 即 Resilient Distributes Dataset(弹性分布数据集), 是spark中最基础、最常用的数据结构。其本质是把input source 进行封装,封装之后的数据结构就是RDD,提供了一系列操作,比如 map、flatMap、filter等。input source种类繁多,比如hdfs上存储的文件、本地存储的文件,相应的 RDD的种类也有很多。不同的input source 对应着不同的RDD类型。比如从hdfs上读取的text对应着HadoopRDD, val hRdd= sc.textFile(“hfs://…….”) 生成的就是HadoopRDD。
1.3 RDD
Spark的理论基础是RDD,RDD让Spark实现了"one stack to rule them all" (一个技术堆栈统一数据处理)的目标。
1.3.1 RDD抽象概念
RDD是弹性分布式数据集的简称,是一个抽象的概念。它主要有以下五个特征:
- 一个分片列表 partition list
- 一个计算函数compute,对每一个split(分片)进行计算
- 对其他RDD的依赖列表dependencies list;依赖又分宽依赖和窄依赖。(可容错)
- partitioner for key-value RDDs.比如说 hash-partitioned rdd(这是可选的,并不是所有的add都会有这个特征)
- 对每一个split计算的优先位置 Preferred Location。比如对一个hdfs文件进行计算时,可以获取优先计算的block locations。
为了便于理解RDD,这里给出比较通俗易懂的描述:
- 首先RDD的定义为RDD[T],可以将RDD理解成T实例的一个集合
- 对应的分区就是将这一组T实例的集合拆分成多个子集,这的子集就是数据分区。数据以Block,即块方式存储在HDFS上,加载后,在Spark中,子集合实际上对应着分区的概念。分区就是将对应大数据量的T实例集合切成多个小数据量的T实例子集合。这个集合,对应的内部代码其实就是Iterator[T]
- 用户构建该RDD的父RDD即是该RDD的父依赖,由于可以有多个父依赖的RDD因此有对应父RDD的一个依赖列表。
- 计算每个分片的函数compute,体现了惰性计算的特性,比如:MapPartionsRDD,对应的compute函数记录了该RDD对父依赖的各个分区的操作,也就是记录了对MapPartionsRDD各个分区的输入员数据进行的计算。
- Key - Value RDD,其实就是T类型为Key - Vaule对的类型。
1.3.2 RDD的操作
RDD的操作分为两类:Transformation与Action。其中Transformation是惰性执行的,惰性执行表示真正需要的时候才会执行,这里实在需要具体的Action去触发才会开始执行,每个Action的触发都会提交一个Job。
一个典型的操作流程图:
RDD操作流程.png
- 首先通过textFile操作从外部存储系统HDFS中读取文件,构建出两个RDD实例A和C;
- 然后A做flapMap和Map转换操作,对C做Map型操作和reduceByKey转换操作;
- 最后对得到的B和E两个做联合操作,并通过saveAsSequenceFile操作将最终的F实例持久化到外部存储系统HDFS上。
Transformation和Action这两者的区别可以从它们的返回值查看,Transformation是将一个RDD转换为新的RDD,而Action操作会将结果反馈到Driver Program或者存储到外部存储系统上。
1.3.3 RDD 的依赖关系
RDD的依赖分为债依赖和宽依赖,如图:
依赖.png
- 其中每一个方框表示一个RDD,其内的阴影表示RDD的分区。
- 对于窄依赖,可以进行pipeline操作,即允许在单个集群节点上流水线式的执行,这个节点可以计算所有父级分区。
- 而在节点失败后的恢复效率上,在窄依赖中,只有在失败节点上丢失的父级分区需要重新计算,并且这些丢失的父级分区可以并行的在不同的节点上重新计算。与此相反,在宽依赖的继承关系中,单个失败的节点可能导致一个RDD的所有祖先RDD中的一些分区丢失,导致计算的重新执行。
对于RDD依赖可以从以下两个方面理解:
- 依赖本身是描述两个RDD之间的关系。但一个RDD可以与多个RDD有依赖关系。
- 宽依赖和窄依赖的判断:在RDD的各个分区中对父RDD的分区的依赖关系
-> 窄依赖:子RDD的每个分区依赖于常数个父分区(与数据规模无关)
-> 宽依赖:子RDD的每个分区依赖于所有的父RDD的分区
1.3.4 一个典型的DAG
Spark将数据在分布式环境下分区,然后讲作业转化为DAG,并分阶段进行DAG的调度和任务的分布式并行处理。
DAG典型示意图如下:
DAG.png
描述了DAG调度时会根据Shuffle将Job划分为Stage,比如A到B之间,以及F到G之间的数据需要经过Shuffle过程,因此A和F是stage的划分点,以及RDD的Lineage(血统)关系。其中,实线圆角方框标识的是RDD,方框中的矩形块为分区。
这里通过G和F这两个RDD间的依赖关系,描述了如何执行Stage。如上图,RDD G对F的依赖为宽依赖,即对应有Shuffle过程,因为对于G这个RDD就会创建一个Stage,这里是Stage3。
RDD的Lineage关系,可以从族谱角度去理解。即描述了RDD是从哪来的,以及怎么来的信息。
RDD G上一个Action的执行将会以宽依赖作为基于为分区来建构各个Stage,对各Stage内部的窄依赖则前后连接构成流水线。
Spark通过Lineage机制实现高容错,基于DAG图,Lineage是轻量级而高效的,操作之间相互具备Lineage的关系,每个操作只关心其父操作,各个分片的数据之间互不影响,出现错误的时候只要恢复单个分片或分区即可。
DAG的Lineage 机制可以从两个方面进行理解:
- RDD之间的数据流图,即RDD的各个分区的数据是从哪来的
- 基于数据流图之上的操作算子流图,即这些数据传递过来的时候经过哪些算子的操作
可以从RDD的抽象概念来解析Lineage的这两个方面的特性:
- RDD的分区列表(数据块列表)和对父RDD的依赖列表:对应RDD类的getPartion和getDependencies,这两个方法记录了改RDD的数据来源,以及来源数据如何获取(对应窄依赖和宽依赖)
其中,RDD各个分区的数据来源可以从外部存储系统或Scala数据集获取,也可以从其他父RDD获取。 - 计算每个分片的函数:对应RDD类的compute方法,该方法记录了该RDD对它各个分区的数据来源进行的计算。
网友评论