引言

相对Hadoop, Spark在处理需要迭代运算的机器学习训练等任务上有着很大性能提升,同时提供了批处理、实时数据处理、机器学习以及图算法等一站式的服务,因此最近大家一起来学习Spark,特别是MLLib

Spark中使用了RDD(Resilient Distributed Datasets, 弹性分布式数据集)抽象分布式计算,即使用RDD以及对应的transform/action等操作来执行分布式计算;并且基于RDD之间的依赖关系组成 lineage以及checkpoint等机制来保证整个分布式计算的容错性。因此,在学习MLLib的时候,很多时候看到的都是RDD的一些操作,而没 有涉及到分布式计算上来,如下面的代码:

// 创建SparkContext对象,作为Spark的Driver应用程序,同Spark集群进行交互
val conf = new SparkConf().setAppName(s"LinearRegression with $params")
val sc = new SparkContext(conf) // 加载libsvm格式的训练、测试instances数据
val examples = MLUtils.loadLibSVMFile(sc, params.input, multiclass = true).cache() // 将数据分为训练集和测试集
val splits = examples.randomSplit(Array(0.8, 0.2))
val training = splits(0).cache()
val test = splits(1).cache() examples.unpersist(blocking = false) // 设置离线训练算法
val updater = params.regType match {
case NONE => new SimpleUpdater()
case L1 => new L1Updater()
case L2 => new SquaredL2Updater()
} val algorithm = new LinearRegressionWithSGD()
algorithm.optimizer
.setNumIterations(params.numIterations)
.setStepSize(params.stepSize)
.setUpdater(updater)
.setRegParam(params.regParam) // 根据设置的训练算法和训练数据集,得到模型
val model = algorithm.run(training) // 使用模型来预测测试集合上对应的值
val prediction = model.predict(test.map(_.features))

由于Spark使用RDD抽象了分布式计算的操作,因此,上面的代码只是涉及到训练集和测试集以 及上面的操作,感觉不到该程序是单机模型训练还是分布式模型训练。因此,有同学提出来问题,这些RDD操作如何进行分布式计算的呢?这涉及Spark分布 式计算执行模型。下面从Spark集群部署和应用程序提交、执行模型来展开介绍Spark如何进行分布式计算。

Spark集群部署和应用程序提交

Spark集群部署

Spark集群是由Cluster Manager和Worker Node组成。当前Spark支持如下三种不同的Cluster Manager:

  1. Standalone – 使用./sbin/start-master.sh和./sbin/start-slave.sh或者./sbin/start-all.sh即可非常容 易地将Spark集群搭建起来,Spark内置的集群资源管理器。当前我们的实验Spark集群是基于Standalone的集群管理模式搭建起来的
  2. Apache Mesos – 基于Mesos资源管理器来管理机器资源
  3. Hadoop Yarn – 基于Hadoop 2里面的资源管理器来管理机器资源

提交Spark应用程序

不管Spark集群是基于什么样资源管理器进行管理,通过spark-submit往Spark 集群上提交应用程序(包括有SparkContext对象的应用程序叫做Driver),提交Driver应用程序的时候,需指定Cluster Manager的地址,所需要的CPU核数、内存数目等。具体例子如下:

# Run on a Spark standalone cluster
./bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://207.184.161.138:7077 \
--executor-memory 20G \
--total-executor-cores 100 \
/path/to/examples.jar \
1000 # Run on a YARN cluster
export HADOOP_CONF_DIR=XXX
./bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn-cluster \ # can also be `yarn-client` for client mode
--executor-memory 20G \
--num-executors 50 \
/path/to/examples.jar \
1000

提交完应用程序之后,即Driver通过Cluster Manager去Worker Node上分配所需要的CPU和内存。从而有了分布式计算所需要的物理资源,即拥有分布在Spark集群中Worker Node上的ExecutorBackend进程,该进程等待来自Driver的Task任务。

下面以Standalone集群模式为例说明整个过程,Master对应的时Cluster Manager, Worker对应为Worker Node. 整个操作过程如下:

  1. Driver通过AppClient向Master发送了RegisterApplication消息来注册Application
  2. Master收到消息之后会发送RegisteredApplication通知Driver注册成功,Driver的接收类还是AppClient
  3. Master接受到RegisterApplication之后会触发调度过程,在资源足够的情况下会向Woker和Driver分别发送LaunchExecutor、ExecutorAdded消息
  4. Worker接收到LaunchExecutor消息之后,会执行消息中携带的命令,执行 CoarseGrainedExecutorBackend类(图中仅以它继承的接口ExecutorBackend代替),执行完毕之后会发送 ExecutorStateChanged消息给Master
  5. Master接收ExecutorStateChanged之后,立即发送ExecutorUpdated消息通知Driver。Driver中的AppClient接收到Master发过来的ExecutorAdded和ExecutorUpdated后进行相应的处理
  6. 启动之后的CoarseGrainedExecutorBackend会向Driver发送RegisterExecutor消息
  7. Driver中的SparkDeploySchedulerBackend(具体代码在 CoarseGrainedSchedulerBackend里面)接收到RegisterExecutor消息,回复注册成功的消息 RegisteredExecutor给ExecutorBackend,并且立马准备给它发送任务

最后,CoarseGrainedExecutorBackend接收到RegisteredExecutor消息之后,实例化一个Executor等待任务的到来

Spark执行模型

Spark执行模型分如下三步:

  1. 创建应用程序计算RDD DAG (Directed acyclic graph,有向无环图)
  2. 创建RDD DAG逻辑执行方案,即将整个计算过程对应到Stage上
  3. 根据上面介绍获取到Executor来进行调度并执行各个Stage对应的ShuffleMapResult和ResultTask等任务。必须是执行一个Stage完成之后,才能往下执行接下来的Stage

下面以WordCount例子来说明Spark执行模型

WordCount Job例子

下面以map-reduce中经典例子WordCount为例解释Spark执行模型,整个WordCount Job是计算README.md这个文档中各个单词出现的频次,并且将最终结果保存到wordcount_result目录中去

val wordCountResult = sc.textFile("README.md", 4)
.flatMap(line => line.split(" ")).map(word => (word, 1))
.reduceByKey(_ + _, 2)
vordCountResult.saveAsTextFile("wordcount_result")

RDD DAG

RDD DAG描述的是各个RDD之间的依赖关系。上面例子从RDD DAG的角度来看如下:

即该RDD DAG主要是包括有MappedRDD->FlatMappedRDD->MappedRDD->ShuffledRDD四个RDD的 转换(Transform), 根据Spark实现,RDD的转换操作是不会提交给Spark集群来执行的,因此,上面的操作必须要由Spark的行为(Action)来触发,因此,在 最后调用saveAsTextFile这个行为来将整个WordCount Job提交到Spark集群中来执行。(备注:所有的Spark的转换、行为操作可以参考文档Spark Programming Guide

RDD DAG逻辑执行方案

RDD DAG只是从整体的RDD角度来查看整个Job的执行过程。在RDD DAG逻辑执行方案,需要查看各个RDD中各个Partition的情况,以及各个RDD的Partition的依赖情况来决定如何划分Stage。

根据RDD论文,将RDD的各个Partition的依赖情况划分为Narrow Dependencies和Wide Dependencies:

  • Narrow Dependencies – parent RDD中的一个Partition最多被child RDD中的一个Partition所依赖
  • Wide Dependencies – parent RDD中的一个Partition被child RDD中的多个Partition所依赖

如图所示,map是Narrow Dependencies, groupByKey是Wide Dependencies。若在Job中存在有Wide Dependencies,就划分为不同的Stage。

具体到WordCount Job,具体的Stage划分如下:

由于flatMap、map等操作对RDD进行转换得到的RDD的partition和 parent RDD的partition是Narrow Dependencies关系,因此处于在同一个Stage中,即都在Stage 1中;而reduceByKey这个转换,其对应的是Wide Dependencies关系,因此,需要新建一个Stage出来,即所在为Stage 2,独立于Stage 1。

当Job执行saveAsTextFile这个行为的时候,其依赖于Stage 2中ShuffledRDD,而Stage 2又依赖于Stage 1,因此,需要先执行完Stage 1中所有的Task之后,才执行Stage 2中的所有的Task。当Stage 2中所有的任务执行完之后,整个Job即执行完成。

RDD Task执行

Spark通过分析各个RDD的依赖关系生成了RDD DAG,然后再通过分析各个RDD中的partition之间的依赖关系来将执行过程进行逻辑划分成不同的Stage。有了这些Stage的依赖关系之 后,从最parent stage开始执行,执行完了parent stage的所有的task再执行child stage中的所有的task,直到所有的Stage都执行完成。

针对WordCount这个例子来看,Stage 2依赖Stage 1,因此,先执行Stage 1中的Task。而Stage 1中各个RDD中是有4个partition(见textFile(“README.md”, 4)中的第二个参数来指定RDD中需要划分多少partition,当然对于RDD也可以通过调用repartition和coalesce来改变 partition数目),因此,在Stage 1中由Driver应用程序生成4个ShuffleMapTask并提交给之前分配得到的Executor中执行。当Stage 1中4个ShuffledMapTask执行完成之后,再开始执行Stage 2中的2个ResultTask(由于reduceByKey(_ + _, 2)中的第二个参数指定只需要2个reduce,因此,在ShuffledRDD中只有2个partition,因此,也只有对应的2个 ResultTask).当Stage 2中的2个ResultTask执行完之后,saveAsTextFile会将ShuffledRDD中的内容落地到文件中中,即保存到 wordcount_result目录中去。从而完成了整个WordCount Job的执行任务。

从上面的描述可以看到,Stage 1中会生成4个ShuffleMapTask, 在提交WordCount Job应用程序给Spark集群时候,获取得到的Executor数目大于等于4个,那么该4个ShuffleMapTask可以在这些Executor 进行并行运行,从而实现了在不同的Executor进行分布式计算。

最后说明下,RDD的Partition数目决定了执行过程中生成多少个Task,即决定于并行 计算的数目,该参数是Spark应用程序中非常重要的参数,Partition设置的越大,并行度越高,在Executor资源有限的情况下,任务之间调 度开销会变大,同时若有Wide Dependencies的时候,Shuffle的代价也比较多,因此在实际应用中需要谨慎调整该参数。Spark作者推荐的“比较合理的 partition数目”为:

  1. 100-10000
  2. 最少要有2倍于申请的CPU核数
  3. 每个Partition对应的Task最少要运行100ms以上

结束语

RDD为Spark抽象了分布式计算的操作,即将任务进行分布式计算转成RDD的转换和行为上。 为了了解Spark究竟如何进行分布式计算的,本文首先介绍提交Driver应用程序给Spark集群,通过同Cluster Manager和Worker Node进行交互,得到该Driver所需要的Executor资源,然后再由Spark应用程序通过分析RDD DAG依赖关系,以及各个RDD之间partition的依赖关系来生成不同的Stage,再将Stage中的任务,按照RDD的partition个数 生成相同数目的Task提交给Executor来执行,从而实现了Task在不同的Executor中进行分布式计算,最终实现整个Driver应用程序 的分布式计算。

Spark分布式计算执行模型的更多相关文章

  1. 【分布式计算】30分钟概览Spark分布式计算引擎

    本文主要帮助初学者快速了解Spark,不会面面俱到,但核心一定点到. Spark是继Hadoop之后的下一代分布式内存计算引擎,于2009年诞生于加州大学伯克利分校AMPLab实验室,现在主要由Dat ...

  2. Spark分布式执行原理

    Spark分布式执行原理 让代码分布式运行是所有分布式计算框架需要解决的最基本的问题. Spark是大数据领域中相当火热的计算框架,在大数据分析领域有一统江湖的趋势,网上对于Spark源码分析的文章有 ...

  3. CLR via C# 摘要一:托管程序的执行模型

    托管程序的执行模型大致如下: 编译源代码为程序集(dll或exe文件),程序集包括了记录相关信息的元数据和IL代码 执行程序集文件时,启动CLR,JIT负责把IL编译为本地代码并执行 IL是微软推出的 ...

  4. CLR执行模型

    好好学习底层运行机制,从CLR via C# 开始. CLR的执行模型: CLR:Common Language Runtime,是一个可由多种编程语言使用的"运行时".CLR的核 ...

  5. ASP.NET执行模型之IIS服务器处理流程

    之前在网上看过很多对这方面的讲解,但个人觉得看下来过于 "深奥",不容易理解,所以想用更简单的方式进行阐述,便于理解. 本次我们重点分析用户请求到页面呈现过程中Web服务器的处理过 ...

  6. Orleans的单线程执行模型

    Orleans在默认情况下只创建一个grain的实例,并以单线程模型执行.如果同一个grain实例,在Orleans存在多个实例,就会产生并发冲突,单线程执行模型就可以完全避免并发冲突了. 但在特殊场 ...

  7. Windows Phone 执行模型概述

    Windows Phone 执行模型控制在 Windows Phone 上运行的应用程序的生命周期,该过程从启动应用程序开始,直至应用程序终止. 该执行模型旨在始终为最终用户提供快速响应的体验.为此, ...

  8. 【Framework】HTTP运行期与页面执行模型

    HTTP运行期 HTTP运行期处理客户端应用程序(例如Web浏览器)进入的一个Web请求,通过处理它的应用程序的适当组件路由请求,然后产生响应并发回提出请求的客户端应用程序. 进入的HTTP Web请 ...

  9. 01.由浅入深学习.NET CLR 基础系列之CLR 的执行模型

    .Net 从代码生成到执行,这中间的一些列过程是一个有别于其他的新技术新概念,那么这是一个什么样的过程呢,有什么样的机制呢,清楚了这些基本的东西我们做.Net的东西方可心中有数.那么,CLR的执行模型 ...

随机推荐

  1. 从汇编的角度看待变量类型与sizeof的机制

    1.动机:前段时间,一直有个疑问,就是编译器是从哪里知道数据的类型的,数据的类型是存在内存里面的么,因为自己调试编译器,发现内存中并没有多余的数据,后来在群上发问,才知道数据在编译成汇编的过程就知道数 ...

  2. Cannot read property 'setState' of undefined

    You're using function() in your Promise chain, this will change the scope for this. If you're using ...

  3. NetCore 下集成SignalR并进行分组处理

    Tips: 1.注意跟普通版Net.MVC的前端处理方式不一样,以前可以connection.start()后直接done里面再做逻辑处理,现在不行了 建议做法是在具体的业务Hub里重写OnConne ...

  4. DBUtils 增删改查例子

    sql CREATE TABLE [dbo].[Person] ( , ) NOT NULL , ) COLLATE Chinese_PRC_CI_AS NULL , [age] [int] NULL ...

  5. Unit07: document 对象 、 自定义对象 、 事件

    Unit07: document 对象 . 自定义对象 . 事件 知识点: <!DOCTYPE html> <html> <head> <meta chars ...

  6. webpack快速入门(三):资源管理

    上一章说了基本的webpack是用,包括命令行打包,npm脚本打包等基础的东西. 这篇说一下webpack的资源管理,包括(图片,字体,数据),首先调整一下项目结构成: webpack-demo |- ...

  7. line 3: /usr/local/arm/4.3.2/bin/arm-none-linux-gnueabi-gcc: No such file or directory

    sudo apt-get install lib32ncurses5(网上下载的很多arm-linux-gcc都是32位的,64位的ubuntu需要按此包)

  8. oracle监听动态注册与静态注册

    client端如果想要连接到远程的数据库服务器,首先数据库服务器必须启动监听器 oracle监听器的配置在$ORACLE_HOME/network/admin/listener.ora,打开这个文件, ...

  9. 重置mysql的root用户密码

    /etc/rc.d/init.d/mysql status /etc/rc.d/init.d/mysql stop mysqld_safe --skip-grant-tables& mysql ...

  10. python开发_python中的变量:全局变量和局部变量

    如果你在为python中的变量:全局变量和局部变量头疼,我想这篇blog会给你帮助 运行效果: 代码部分: #Python中的变量:全局变量和局部变量 #在很多语言中,在声明全局变量的时候,都喜欢把全 ...