欢迎阅读美图数据技术团队的「Spark,从入门到精通」系列文章,本系列文章将由浅入深为你们介绍 Spark,从框架入门到底层架构的实现,相信总有一种姿式适合你,欢迎你们持续关注:)
Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架,是专为大规模数据处理而设计的快速通用的大数据处理引擎及轻量级的大数据处理统一平台。java
当咱们在谈 Spark 的时候多是指一个 Spark 应用程序,替代 MapReduce 运行在 Yarn上,存储在 HDFS 上的一个大数据批处理程序;也多是指使用包含 Spark sql、Spark streaming 等子项目;甚至 Tachyon、Mesos 等大数据处理的统一平台,或者称为 Spark 生态。算法
图 1
sql
发展至今,Spark 已不只仅是 MapReduce 的替换方案,它已经发出成了一个包含众多子项目的 Spark 生态。如图 1 所示,Spark 生态可分为四层:数据库
数据存储层,以 HDFS 、Tachyon 为表明的一些分布式文件存储系统或各类数据库;
编程
资源管理层,Yarn、Mesos 等资源管理器;
后端
数据处理引擎;
缓存
应用层,以 Spark 为基础产生的众多项目;安全
Spark SQL 提供 HiveQL(经过 Apache Hive 的 SQL 变体 Hive 查询语言)与Spark 进行交互的 API。每一个数据库表被当作一个 RDD,Spark SQL 查询被转换为 Spark 操做。Spark Streaming 对实时数据流进行处理和控制,它容许程序可以像普通 RDD 同样处理实时数据。bash
接下来的系列文章将会详细介绍 Spark 生态中的其余模块与各个子项目,接下来将经过与 MapReduce 的对比来介绍数据处理引擎Spark的特色及其原理。网络
根据谷歌和百度的搜索结果显示,Spark 的搜索趋势已与 Hadoop 持平甚至赶超,标志着 Spark 已经成为计算部分的事实标准,也就是说大数据技术绕不开 Spark 了。
在大数据的存储、计算、资源调度中,Spark 主要解决计算问题,即主要替代 Mapreduce 的功能,底层存储和资源调度不少公司仍然选择使用 HDFS、Yarn 来承载。为何众多企业在 Hadoop 生态框架里都选择用 Spark 做为处理引擎?让咱们仔细看看它有什么特色。
1.速度快。Spark 基于内存进行计算( 也有部分计算基于磁盘) ;
2.容易上手开发。 Spark 基于 RDD 的计算模型, 比 Hadoop 基于 Map-Reduce 的计算模型要更易于理解、易于上手开发实现各类复杂功能,如二次排序、 topN 等复杂操做时更加便捷。;
3.超强的通用性。 Spark 提供了 Spark RDD、 Spark SQL、 Spark Streaming、 Spark MLlib、 Spark GraphX 等技术组件, 能够一站式地完成大数据领域的离线批处理、 交互式查询、 流式计算、 机器学习、图计算等常见的任务;
4.集成 Hadoop。 Spark 能够完美集成 Hadoop。 Hadoop 的 HDFS、 Hive、HBase 负责存储, Yarn 负责资源调度, Spark 负责大数据计算是比较流行的大数据解决方案。
4.极高的活跃度。 Spark 目前是 Apache 基金会的顶级项目, 全世界有大量的优秀工程师是 Spark 的 committer, 而且世界上不少顶级的 IT 公司都在大规模地使用Spark。
看看一样是负责计算问题的 MapReduce,如图 2 所示是 MapReduce 计算 WordCount。
图 2
MapReduce 解决了大数据处理中多种场景问题,可是它的局限性也很明显:
MapReduce 只提供 Map 和 Reduce 两个操做,欠缺表达力,复杂的计算须要大量的 Job 才能完成。
中间结果也放在 HDFS 文件系统中,迭代计算的话效率很低。
适用 Batch 数据处理,对于交互式数据处理而言实时数据处理的支持不够。
须要写不少底层代码,难上手。如上所示的 WordCount 程序至少须要三个 java 类:Map 类、Reduce 类、Job 类,这里不详细列出。
许多项目针对它的局限性进行了改进(如 Tez 等),接着看图 3 中 Spark 的具体操做流程:
图 3
首先咱们能够看到 Spark 提供了丰富的算子(textFile、FlatMap、Map、ReduceByKey 等),在计算的中间结果也没有存储到 HDFS 的操做。而后,对于上图的 WordCount 程序,Spark 只须要以下一行代码:
sc.textFile(s"${path}").flatMap(_.split(" ")).map(word => (word, 1)).reduceByKey(_ + _).saveAsTextFile("hdfs://xxx")复制代码
图 4 列举了 Spark 和 MapReduce 做为数据处理引擎的一些对比。值得一提的是关于数据处理的规模,Spark 在诞生后,社区里有不少质疑 Spark 处理数据规模的声音,随后官方给出了对于一 PB 数据排序的实验,而且处理时间打破了当时的记录。但咱们也不能忽视,在实际生产过程当中,咱们面对的不是一个程序或者一个任务,在同一个集群,若是有不少的 Spark 程序没有获得很好的优化,会浪费大量的内存,从而让一些程序须要排队等待,在这种状况下,Spark 处理的数据规模可能会小于 MapReduce 处理的数据规模。(以后的系列文章也会介绍关于 Spark 内存调优的相关内容)
图 4
关于最后一点容错性,MapReduce 中每一步操做的结果都会被存入磁盘,在计算出现错误时能够很好的从磁盘进行恢复;Spark 则须要根据 RDD 中的信息进行数据的从新计算,会耗费必定的资源。Spark 提供两种方式进行故障恢复:经过数据的血缘关系再执行一遍前面的处理;Checkpoint 将数据集存储到持久存储中。理论上若是选择在每一个完成的小步骤上加 CheckPoint,Spark 的容错性能能够和 MR 达到同样的稳健。固然,不多有人会这么作。
咱们经过 Spark 与 MapReduce 对比。看到了 Spark 对 MapReduce 局限性的改进,还有它快速、通用的特色。接下来将经过 Spark 的设计思想和执行过程来讲明它为何能够作到这些特色。
图 5
如图 5 所示,在 Spark 集群中由一个节点做为 driver 端建立 SparkContext。Spark 应用程序的入口负责调度各个运算资源,协调各个 Worker Node上 的 Executor。根据用户输入的参数会产生若干个 workr,workr 节点运行若干个 executor,一个 executor 是一个进程,运行各自的 task,每一个 task 执行相同的代码段处理不一样的数据。
图 6
如图 6 所示是 Spark 的具体执行过程,client 提交做业,经过反射 invoke 执行用户代码 main 函数,以后开始启动 CoarseGrainedExecutorBackend 和初始化 SparkContext。
*SparkContext 初始化包括初始化监控页面 SparkUI、执行环境 SparkEnv、安全管理器 SecurityManager、stage 划分及调度器 DAGScheduler、task 做业调度器 TaskSchedulerImpl 、与 Executor 通讯的调度端 CoarseGrainedSchedulerBackend。
DAG Scheduler 将做业划分后,依次提交 stage 对应的 taskSet 给 TaskSchedulerImpl,TaskSchedulerImpl 会 submit taskset 给 driver 端的 CoarseGrainedSchedulerBackend 后端,接着 CoarseGrainedSchedulerBackend 会一个一个的 LaunchTask。在远端的 CoarseGrainedExecutorBackend 接收到 task 提交 event 后,会调用 Executor 执行 task,最终 task 是在 TaskRunner 的 run 方法内运行。
那么在过程 4 中 DAG Scheduler 如何划分做业?若是产生 stage、task 等给 Executor 执行呢?接着咱们看做业划分执行的示例。
图 7
图 7 描述了一个 Spark 程序,从 HDFS 上读取数据产生 RDD-A 而后 flatmap 操做到 RDD-B,读取另外一部分数据的到RDD-C,而后 map 操做的到 RDD-D,RDD-D 聚合操做 RDD-E,RDD-B 和 RDD-E 加入后获得 RDD-F,而后再将结果存储到 HDFS 上。
Spark 根据 RDD 之间的不一样点依赖关系切分红不一样的阶段(Stage),途中有四个阶段,其中 Stage0 和 Stage2 因为没有依赖关系是能够并行执行的。但Stage2须要等待Stage1执行完毕。RDD-D 到 RDD- F 的聚合操做以及 Stage0 和 Stage2 获得的 RDD- B 和 RDD-E join在一块儿的到 RDD-F,这个过程会产生 shaffle。没有依赖关系的Stage是能够并行执行的,可是对于job,Spark是串行执行的,若是想要并行执行Job,能够在Spark程序中进行多线程编程。
在这个 DAG 图中,Spark 可以充分了解数据之间的血缘关系,这样某些任务失败后能够根据血缘关系从新执行计算获取失败了的 RDD。
* 宽依赖和窄依赖
窄依赖是指父RDD的每一个分区只被子RDD的一个分区所使用,子RDD分区一般对应常数个父RDD分区;
宽依赖是指父RDD的每一个分区均可能被多个子RDD分区所使用,子RDD分区一般对应全部的父RDD分区。这个概念在下面的例子中会涉及。
Spark 提供了丰富的算子,操做也更加通用。那么这种划分做业、执行并行计算的方案如何使 Spark 产生基于内存计算的快速效果呢?都说 Spark 擅长迭代计算,那么咱们经过一个经典的迭代问题 PageRank 算法来与 MapReduce 比较一下。
图 8,via http://www.jos.org.cn/jos/ch/reader/create_pdf.aspx?file_no=5557&journal_id=jos
图 8 是 MapReduce 进行 pagerank 算法的一次迭代过程,须要注意的是灰色的部分都是须要存储到磁盘的数据。
图 9 ,via http://www.jos.org.cn/jos/ch/reader/create_pdf.aspx?file_no=5557&journal_id=jos
图 9 所示是 Spark 执行 pageRank 算法的一次迭代过程,相较于 MapReduce 作了不少改进。首先在内存足够的状况下 Spark 容许用户将经常使用的数据缓存到内存中,加快了系统的运行速度;其次 Spark 对数据之间的依赖关系有了明确的划分,根据宽依赖与窄依赖关系进行任务的调度,能够实现管道化操做,使系统灵活性得以提升。
图 10:MapReduce 进行 pagerank 算法的二次迭代,via http://www.jos.org.cn/jos/ch/reader/create_pdf.aspx?file_no=5557&journal_id=jos
图 11:Spark 进行 pagerank 算法的二次迭代,via http://www.jos.org.cn/jos/ch/reader/create_pdf.aspx?file_no=5557&journal_id=jos
如图所示 Spark 能够将具备窄依赖关系的 RDD 分区分配到一个任务中,进行管道化操做,任务内部数据无需经过网络传输且任务之间互不干扰,所以 Spark 两次迭代只有三次 shuffle。
在一次迭代过程当中,MapReduce 与 Spark 在性能上可能并无很大的差异,可是随着迭代次数的增长,二者的差距逐渐显现出来。Spark 根据依赖关系采用的任务调度策略使得 shuffle 次数相较于 MapReduce 有了显著下降,所以 Spark 的设计十分适合进行迭代运算。
回顾本篇文章,咱们依次从概念、特色及原理三个角度初步介绍了 Spark,下一篇咱们将具体介绍 Spark on Yarn 的运做流程与机制,敬请期待。
附:Spark 相关术语表