Sparkr入门(二)------Spark架构

一、概述

传统的Hadoop MapReduce计算模型适用于计算非迭代任务,因为MR得中间结果是存放在本地磁盘上的,这样做是为了防止reduce任务执行失败。所以,当使用迭代任务时,就需要多次访问磁盘,造成效率低下。如果把这部分需要重复使用的数据放在内存中,则可以省去很多次访问磁盘的时间。

Spark则是使用RDD作为数据抽象,可以通过cache()可以将当前RDD所代表的数据存储在内存中,使得我们可以在内存中直接重用之前的数据。所以,Spark很适合用于迭代计算,这也是Spark任务处理速度比MR快的原因之一。

二、编程模型

1.RDD

RDD是一组对象的只读集合,所有的转换操作都是基于一个RDD重新构建另一个RDD。RDD不需要存储在外存上,因为RDD得句柄包含了足够的信息,可以通过这些信息从某个RDD或者原始数据进行计算重构RDD。所以不需要向Hadoop那样需要把所有的中间结果存在外存上,访问效率就高了很多。

RDD有四种构建方式:

  • 通过文件系统上的文件进行构建
  • 通过集合对象创建,例如scala或者java中的Collection类
  • 通过现有的RDD进行转化得到新的RDD
  • 通过改变现有RDD的持久性。(cache操作和save操作,cache将RDD缓存到内存中以供后面使用,save将RDD保存到文件系统上),这里cache虽然可以将RDD缓存到内存中,但是如果RDD过大,当内存不足时(可能别的运行中的占用,空余的内存不足),将只是给则个RDD一个标记,表示这个RDD可能需要复用,当复用时会重新计算(这样是为了在数据集很大时,spark可以继续工作)。

其实,本质上来说,RDD只是一个数据结构,并不存储真正的数据,只是通过RDD可以得到我们想要的数据而已。RDD存储了获取数据的方法(数据的计算转化过程,即记录自己的父RDD是谁,以及如何转化),以及数据如何分区的方法还有就是数据的类型。

2、共享变量

我们客户端与Spark通信是通过一个SparkContext类型的变量,spark中的各个RDD转换操作采用了函数式编程,需要我们向其中传递函数和数据,集群中的每个任务都会得到这些数据一个副本,更新这些副本数据不会影响我们驱动器程序中的变量,所以Spark提供了累加器广播变量两个共享变量。

累加器用于将各个工作节点中的值聚合到驱动器程序中。

广播变量使得我们可以高效的向所有的工作节点发送一个较大的只读值,以供一个或多个spark操作使用,避免每个操作都需要向所有节点发送一次这个数据。

三、实现

Spark构建在Mesos之上,Mesos是一个"集群操作系统",它允许多个并行应用程序以细粒度的方式共享一个集群。

所有的RDD都实现了同一个接口,这个接口包含三个方法:

  • getPartitions:返回该RDD的分区列表
  • getIterator(partition):返回一个遍历该RDD某个分区的迭代器
  • getPreferredLocations(partition):用于任务调度,以实现将任务发送到最近的数据节点上。

每当对RDD进行并行操作时,Spark都会创建一个任务来处理该数据集所划分的每个分区,将任务发送到包含这些分区的节点上(计算向数据移动)。首先得到所有的分区列表,然后根据每个分区计算出分区所在的最近节点,然后把任务发送到对应节点,使用getIterator,将该节点所含的分区ID传入,遍历该分区的数据,进行操作。

广播变量的实现,是将广播变量存在文件系统中,同时将文件的路径序列化传到工作节点,当工作节点使用广播变量时,先检查其是否在缓存中,如果不在则反序列文件路径,然后去对应的路径上取数据。

累加器,则是在创建时被赋予一个唯一的ID,当工作节点运行任务后,工作节点会向驱动程序发送一条消息,其中包含自己对累加器所做的全部更新,驱动程序对每个更新操作在每个分区应用一次(防止由于失败而重新执行任务出现重复计数)。

Spark集成到Scala,Scala解释器通常将用户输入的每一行都编译为一个类来操作,此类是一个单例对象,包含该行上的数据或函数,并在其构造器中运行该行代码,例如:

     var x = 5;
     println(x)//解释器会定义一个包含x的类(例如Line1),并为x赋值5,
    //然后在第二行构造类Line2的构造器执行println(Line1.getInstance().x) 

同时,Spark稍微更改了一下这种解释方式,第一是将每行生成的单例对象存储到了一个公共的文件系统上,同时生成的单例对象直接引用前几行生成的单例对象,而不是通过前几行生成的单例对象调用getInstance方法来获得引用。

四、Spark架构

Spark生态包括Spark Core、Spark SQL、Spark Streaming、MLib、GraphX及部分。

4.1 生态架构图

在这里插入图片描述

  • Spark Core:包含Spark的基本功能,例如RDD的定义以及基于RDD进行转换和行动的API,是一个对由很多计算任务组成的、运行在多个工作机器上或者集群上的应用进行调度、分布与监控的计算引擎,包含任务调度、内存管理、错误恢复、与存储系统交互等功能。是其余Spark库的基础。
  • Spark SQL:用来操作结构化数据的程序包。可以使用HQL来查询数据。
  • Spark Streaming:提供对实时数据进行流式计算的组件。
  • MLib:spark提供的常见的机器学习库
  • GraphX:图计算相关的程序库

4.2 Spark Core的架构组成

在这里插入图片描述

  • Cluster Manager:在单节点模式即为Master主节点,负责控制整个集群,监控Worker,如果Spark部署在YARN上,则为资源管理器。
  • Worker节点:从节点,负责控制计算节点,启动Executor或者Driver,YARN上的NodeManager节点。
  • Driver:运行Application的main()函数
  • Executor:执行器,为某个Application运行在某个Worker上的一个进程

在Spark中,一个应用(Application)由一个任务控制节点(Driver)和若干个作业(Job)构成,一个作业由多个阶段(Stage)构成,一个阶段由多个任务(Task)组成。当执行一个应用时,任务控制节点会向集群管理器(Cluster Manager)申请资源,启动Executor,并向Executor发送应用程序代码和文件,然后在Executor上执行任务,运行结束后,执行结果会返回给任务控制节点,或者写到HDFS或者其他数据库中。

4.3Spark任务执行流程

在这里插入图片描述
具体运行流程:

(1)当一个Spark应用被提交后,即执行main函数,启动main函数的这个节点就成为了该应用的Driver节点,然后Driver创建一个SparkContext(在Driver节点上)。

(2)SparkContext向Cluster Manager注册并提交任务,然后申请运行Executor的资源。

(3)资源管理器(Cluster Manager)会与所有Worker通信,找出空闲的Worker,为Executor分配节点和资源,启动Executor进程,Executor向资源管理器发送心跳。

(4)Executor向SparkContext申请Task

(5)SparkContext根据RDD的依赖关系构建DAG图,然后将DAG提交给DAG调度器(DAGScheduler)进行解析,将DAG分为成多个“阶段”(每个阶段一个任务集),并且计算出各个阶段之间的依赖关系,然后将任务集(Taskset,即Stage)提交给下层的TaskSchedule(任务调度器)进行处理并派发任务。

(6)Task Scheduler将Task发送给Executor执行,同时SparkContext将应用程序代码发送给Executor

(7)Executor执行完毕,将结果返回给任务调度器,然后由任务调度器返回给DAG调度器,最后返回给用户,然后释放资源。

详细流程图:
在这里插入图片描述

4.4 Stage的划分

窄依赖:父RDD的每一个分区最多被一个子RDD的分区使用,可以理解为独生子女。可以是一个父RDD转换为一个子RDD的分区,或者多个父RDD的分区对应一个子RDD的分区,例如Spark中的map算子。

宽依赖:除窄依赖以外的所有依赖,例如子RDD的分区依赖父RDD的所有分区,一个父RDD对应多个子RDD,即需要shuffle的情况,例如groupByKey等操作。

Stage:Stage划分是按照shuffle操作来划分的,即按宽依赖划分stage,shuffle之前的所有操作算作一个stage,shuffle之后的所有操作算另一个stage。

Spark的DAG调度器会将RDD的依赖关系转换为DAG图,对于窄依赖,由于分区是一对一的(对于一对父子),所以分区转换可以在一个线程内完成,可以划分到一个stage中。宽依赖,需要spark在所有分区上进行shuffle,所以会打乱所有分区上原有数据分布,需要在每个worker上进行,只有等shuffle完成,下一个操作才能继续,所以stage按照宽依赖划分。

Spark会根据DAG图,从后向前推进,遇到一个宽依赖就划分出一个stage,遇到窄依赖就将其加入当前stage。Task的数量由每个stage中最后一个操作所转换出来的RDD的分区数量决定的(每个分区跑一个Task)

4.5 优点

总体而言,Spark运行架构具有以下特点:
(1)每个应用都有自己专属的Executor进程,并且该进程在应用运行期间一直驻留。Executor进程以多线程的方式运行任务,减少了多进程任务频繁的启动开销,使得任务执行变得非常高效和可靠;
(2)Spark运行过程与资源管理器无关,只要能够获取Executor进程并保持通信即可;
(3)Executor上有一个BlockManager存储模块,类似于键值存储系统(把内存和磁盘共同作为存储设备),在处理迭代计算任务时,不需要把中间结果写入到HDFS等文件系统,而是直接放在这个存储系统上,后续有需要时就可以直接读取;在交互式查询场景下,也可以把表提前缓存到这个存储系统上,提高读写IO性能;
(4)任务采用了数据本地性和推测执行等优化机制。数据本地性是尽量将计算移到数据所在的节点上进行,即“计算向数据靠拢”,因为移动计算比移动数据所占的网络资源要少得多。而且,Spark采用了延时调度机制,可以在更大的程度上实现执行过程优化。比如,拥有数据的节点当前正被其他的任务占用,那么,在这种情况下是否需要将数据移动到其他的空闲节点呢?答案是不一定。因为,如果经过预测发现当前节点结束当前任务的时间要比移动数据的时间还要少,那么,调度就会等待,直到当前节点可用。

参考博客:
http://dblab.xmu.edu.cn/blog/972-2/
https://blog.csdn.net/qq_37332702/article/details/88687251

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值