Spark在2012年左右开始流行,那时内存的容量提升和成本降低已经比MapReduce出现的十年前强了一个数量级,Spark优先使用内存的条件已经成熟;其次,使用大数据进行机器学习的需求越来越强烈,不再是早先年那种数据分析的简单计算需求。而机器学习的算法大多需要很多轮迭代,Spark的stage划分相比Map和Reduce的简单划分,有更加友好的编程体验和更高效的执行效率。
实际上在Spark诞生前,大家并没有觉得Hadoop的MapReduce慢,毕竟是做大数据计算,执行时间也还在能够接受的范围之内。直到Spark的出现,通过对比,人们才发现,原来可以快这么多。
除了速度快外,Spark还有简单易用的编程模型。
使用Scala语言在Spark上编写WordCount程序,主要代码只需如下三行:
val textFile = sc.textFile("hdfs://...")
val counts = textFile.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://...")
第1行代码:根据HDFS路径生成一个输入数据RDD。
第2行代码:在输入数据RDD上执行3个操作,得到一个新的RDD。
第3行代码:将这个RDD保存到HDFS。
RDD是Spark的核心概念,它既是Spark面向开发者的编程模型,又是Spark自身架构的核心元素。
大数据计算就是在大规模的数据集上进行一系列的数据计算处理。MapReduce针对输入数据,将计算过程分为两个阶段,Map和Reduce,可以理解成是面向过程的大数据计算。我们在用MapReduce编程的时候,思考的是,如何将计算逻辑用Map和Reduce两个阶段实现,map和reduce函数的输入和输出是什么。
Spark是直接针对数据进行编程,将大规模数据集合抽象成一个RDD对象,然后在这个RDD上进行各种计算处理,得到一个新的RDD,继续计算处理,直到得到最后的结果数据。Spark可以理解成是面向对象的大数据计算。
我们在进行Spark编程的时候,思考的是一个RDD对象需要经过什么样的操作,转换成另一个RDD对象,思考的重心和落脚点都在RDD上。
重新看上面的代码实际上是执行了3次RDD的转换,因为转换的RDD可以继续调用RDD的转换函数,所以连续写成了一行代码。
RDD上定义的函数分为2种:
RDD上的转换操作分成两种:
Spark应用程序代码中的RDD和Spark执行过程中生成的物理RDD不是一一对应的。
以Spark为基础,有支持SQL语句 Spark SQL,有支持流计算的Spark Streaming,有支持机器学习的MLlib,还有支持图计算的GraphX。利用这些产品Spark技术栈支撑起大数据分析、大数据机器学习等各种大数据应用场景。
MapReduce一个应用一次只运行一个map和一个reduce。
Spark可以根据应用的复杂程度,分割成更多的计算阶段(stage),这些计算阶段组成一个有向无环图DAG,Spark任务调度器可以根据DAG的依赖关系执行计算阶段。
以机器学习中的逻辑回归性能为示例,我们发现Spark比MapReduce快100多倍,因为机器学习算法需要进行大量的迭代计算,产生数万个计算阶段,这些计算阶段Spark在1个应用中处理完成,而MapReduce需要启动数万个应用。
有向无环图-DAG:不同阶段的依赖关系是有向的,计算过程只能沿着依赖关系方向执行,被依赖的阶段执行完成之前,依赖的阶段不能开始执行,同时,这个依赖关系不能有环形依赖,否则会成为死循环。
下面这张图描述了一个典型的Spark运行DAG的不同阶段:
整个应用被切分成3个阶段,阶段3需要依赖阶段1和阶段2,阶段1和阶段2互不依赖。Spark在执行调度的时候,先执行阶段1和阶段2,完成以后,再执行阶段3。如果有更多的阶段,Spark的策略也是一样的。只要根据程序初始化好DAG,就建立了依赖关系,然后根据依赖关系顺序执行各个计算阶段,Spark大数据应用的计算就完成了。
负责Spark应用DAG生成和管理的组件是DAGScheduler,它根据程序代码生成DAG,然后将程序分发到分布式计算集群,按计算阶段的先后关系调度执行。
有的函数有时候有shuffle,有时候没有。比如上图例子中RDD B和RDD F进行join,得到RDD G,这里的RDD F需要进行shuffle,RDD B就不需要。
不需要进行shuffle的依赖,在Spark里被称作窄依赖;
需要进行shuffle的依赖,被称作宽依赖。
跟MapReduce一样,shuffle也是Spark最重要的一个环节,只有通过shuffle,相关数据才能互相计算,构建起复杂的应用逻辑。
虽然其本质依然是Map和Reduce,但这种多个计算阶段依赖执行的方案可以有效减少对HDFS的访问,减少作业的调度执行次数,因此执行速度也更快。
Hadoop MapReduce主要使用磁盘存储shuffle过程中的数据不同,Spark优先使用内存进行数据存储,包括RDD数据。除非是内存不够用了,否则是尽可能使用内存, 这也是Spark性能比Hadoop高的另一个原因。
Spark支持Standalone、Yarn、Mesos、Kubernetes等多种部署方案,几种部署方案原理也都一样,只是不同组件角色命名不同,但是核心功能和运行流程都差不多。