在 Hadoop1 中,MapReduce 计算框架即负责集群资源的调度,还负责 MapReduce 程序的运行。

一,MapReduce 组成

MapReduce 的运行过程有三个关键进程:

1,大数据应用进程。这是用户启动的 MapReduce 程序进程,主要是指定 Map 和 Reduce 类、输入输出文件路径等,并提交作业给 Hadoop 集群的 JobTracker 进程。

2,JobTracker 进程。这是 Hadoop 集群的常驻进程,且全局唯一。主要负责集群资源分配和任务调度。该进程会根据输入数据量的大小,命令集群中每个 TaskTracker 进程启动相应数量的 Map 进程和 Reduce 进程。

3,TaskTracker 进程。主要负责启动和管理 Map 进程或者 Reduce 进程。因为需要每个数据块都有对应的 map 函数,所以 TaskTracker 进程通常和 HDFS 中 DataNode 进程启动在同一个服务器。也就是说 Hadoop 集群中大多数节点上即运行着 DataNode 进程也运行着 TaskTracker 进程。

在 Hadoop1 中 MapReduce 框架即负责集群的资源调度,还负责 MapReduce 程序的运行。由于这种架构资源调度和计算高度耦合,导致了一个 Hadoop 集群中只能跑 MapReduce 计算任务,无法跑其它的计算任务,维护成本很高。

在后面的 Hadoop2 中改成了 Yarn + MapReduce 架构,将资源的调度工作交给了 Yarn,MapReduce 只负责计算。这样就能保证 Hadoop 集群技能跑 MapReduce 计算任务,还能跑任何支持 Yarn 资源调度的计算任务,比如 Spark,Storm 等。

二,MapReduce 工作流程

上面已经讲了 Hadoop1 中 Mapreduce 运行过程中的三个进程,可以看出 JobTracker 和 TaskTracker 进程是主从关系,主服务器通常只有一台,从服务器有很多台,所有从服务器听从主服务器的调度。主服务器主要负责整个集群资源的分配以及 MapReduce 作业的任务调度,从服务器负责执行具体的任务。

具体的资源分配,作业调度,具体的作业执行过程,先看下图:



整个过程大概可以概括为:

1,用户启动大数据应用进程,应用进程 JobClient 将用户作业 Jar 包存储到 HDFS 中,将来这些 jar 包会分发给集群中的服务器执行 MapReduce 计算;

2,应用程序提交作业 job 给 JobTracker;

3,JobTracker 根据作业调度算法创建 JobInprocess 树,每个作业都会有一个自己的 JobInprocess 树;

4,JobInprocess 会根据输入数据片的数目和设置的 Reduce 数目创建相应数量的 TaskInprocess;

5,JobTracker 进程和 TaskTracker 进程进行定时通信;

6,如果 TaskTracker 上有空闲的计算资源,JobTracker 会根据该 TaskTracker 节点的资源和需要分配任务的输入数据块所在节点的情况分配相应的 TaskTracker 计算任务;

7,TaskTracker 收到任务后根据任务类型(是 Map 还是 Reduce)和任务参数 (作业的 jar 包路径、输入文件的路径、要处理的数据在数据库中的起始偏移量和结束偏移量等),启动相应的 Map 进程和 Reduce 进程;

8,Map 进程或者 Reduce 进程启动后,检查本地是否有执行任务的 jar 包文件,如果没有就去 HDFS 上去下载,然后加载 Map 或者 Reduce 代码执行;

9,如果是 Map 进程,就会从 HDFS 读取数据 (通常情况下对应的数据就存储在本机) ,如果是 Reduce 进程就将数据输出到 HDFS 。

三,Spark 的执行流程

Spark 的运行流程和 MapReduce 一个应用一次只运行一个 map 和一个 reduce 不同,Spark 可以根据应用的复杂程度,分割成更多的阶段 (stage) ,这些计算阶段组成一个有向无环图 DAG,Spark 任务调度器可以根据 DAG 的依赖关系执行计算阶段。

所谓的 DAG 就是有向无环图,就是说不同的阶段的依赖关系是有向的,计算过程只能沿着依赖关系方向执行,被依赖的阶段执行完成之前,依赖的阶段不能开始执行,同事依赖关系不能有环型依赖,否则就成为死循环了。下图描述了一个典型的 Spark 运行的 DAG 的不同阶段。



从图上看,整个应用被切分成三个阶段,阶段 3 要依赖阶段 1 和阶段 2,阶段 1 和阶段 2 互相不依赖。Spark 在执行调度的时候,先执行阶段 1 和阶段 2 ,完成以后,再执行阶段 3.如果有更多的阶段,Spark 的策略也是一样的。只要根据程序初始化好 DAG,就建立了依赖关系,然后根据依赖关系顺序执行各个计算阶段,Spark 大数据应用的计算就完成了。

负责 Spark 应用的 DAG 生成和管理的组件是 DAGScheduler,DAGScheduler 根据 RDD 之间的依赖关系生成 DAG,每个 stage 的划分依据是 shuffle,每运算到一个涉及到 shuffle 的 RDD 算子就会划分成一个 stage。然后会为每个 stage 分配一定数量的计算任务。

具体的执行过程如下图:



大体过程是这样:

1,Spark 应用程序启动在自己的 JVM 进程里,即 Driver 进程,启动后调用 SparkContext 初始化执行配置和输入数据。SparkContext 启动 DAGScheduler 构造执行的 DAG 图,切分成最小的执行单位:task;

2,然后 Driver 向 Cluster Manager 请求计算资源,用于 DAG 分布式计算。Cluster Manager 收到请求后,将 Driver 的主机地址等信息通知给集群的所有节点 Worker;

3,Worker 收到信息后,根据 Driver 的主机地址,根 Driver 通信并注册,然后根据自己的空闲资源领任务,Driver 会根据 DAG 图开始向注册的 Worker 分配任务;

4,Worker 收到任务后,启动 Executor 进程开始执行任务。

四,MapReduce 和 Spark 的异同

其实从本质上讲,Spark 可以算作一种 MapReduce 计算模型的不同实现。Hadoop MapReduce 简单粗暴的根据 shuffle 将大数据计算分成 Map 和 Reduce 两个阶段,然后就完事了。

而 Spark 相对更加细腻,将前一个的 Reduce 和后一个的 Map 连接起来,作为一个阶段持续计算,形成一个更加高效的计算模型,虽然本质依然是 Map 和 Reduce。但这种多个计算阶段依赖执行的方案可以有效的减少对 HDFS 的访问,减少作业的调度执行次数,因此执行速度更快。

并且和 Hadoop MapReduce 主要使用磁盘存储 shuffle 过程中的数据不同,Spark 有限使用内存进行数据存储,包括 RDD 数据。除非内存不够用了,否则尽可能使用内存,这也是 Spark 性能比 Hadoop 高的另一个原因。

总结来说,MapReduce 的计算模型更偏向于面向过程编程,Spark 的编程模型更偏向于面向对象编程。Spark 有三个主要特性:RDD 编程模型更简单DAG 切分的多阶段计算过程更快速使用内存存储中间计算结果更高效。这三个特性使得 Spark 相对 Hadoop MapReduce 可以有更快的执行速度,以及更简单的编程实现。