引言

相对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. Errors running builder 'DeploymentBuilder' on project ' 解决方法

    此问题一般发生在Myeclipse 保存文件并自动部署时候. Errors occurred during the build. Errors running builder 'DeploymentB ...

  2. 轻量级封装DbUtils&Mybatis之二Dbutils

    DbUtils入门 Apache出品的极为轻量级的Jdbc访问框架,核心类只有两个:QueryRunner和ResultSetHandler. 各类ResultSetHandler: ArrayHan ...

  3. Unit03: Spring Web MVC简介 、 基于XML配置的MVC应用 、 基于注解配置的MVC应用

    Unit03: Spring Web MVC简介 . 基于XML配置的MVC应用 . 基于注解配置的MVC应用 springmvc (1)springmvc是什么? 是一个mvc框架,用来简化基于mv ...

  4. MongoDB在windows平台分片集群部署

    本文转载自:https://www.cnblogs.com/hx764208769/p/4260177.html 前言-为什么我要使用mongodb 最近我公司要开发一个日志系统,这个日志系统包括很多 ...

  5. (转)winform安装项目、安装包的制作、部署

    本文转载自:http://zhan.renren.com/cxymst?gid=3602888498037535727&from=post&checked=true 1,解决方案—添加 ...

  6. MySQL COUNT(*) & COUNT(1) & COUNT(col) 比较分析

    在面试的时候我们会经常遇到这个问题: MySQL 中,COUNT(*).COUNT(1).COUNT(col) 有区别吗? 有区别. 接下来我们分析一下这三者有什么样的区别. 一.SQL Syntax ...

  7. [转] Jsp 重点

    讲师:传智播客 方立勋 4个域对象: pageContext | page 域 request | request 域 session | session 域 servletContext | app ...

  8. 淘宝开源Web服务器Tengine基本安装步骤

    Tengine 是由淘宝核心系统部基于Nginx开发的Web服务器,它在Nginx的基础上,针对大访问量 网站的需求,添加了很多功能和特性.Tengine的性能和稳定性已经在大型的网站如淘宝网,淘宝商 ...

  9. ngui自适应

    增加UIROOT using UnityEngine; namespace Com.Xyz.UI { [ExecuteInEditMode] [RequireComponent(typeof(UIRo ...

  10. Mybatis动态构建Sql(无实体类)

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑. 例如,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空,此时查询 ...