MapReduce是一种框架,所谓框架,也即是一个“条条框框”。那么MapReduce的“条条框框”如下:
1、每启动一个任务,就会启动一个JVM,JVM启动是非常耗时的操作,因为一个JVM需要加载很多数据比如很多jar,很多类等等。
2、中间结果要基于磁盘来排序,因为reduce只能读以key排好序的数据,所以MapReduce需要花费大量时间(主要是磁盘IO)在排序环节。
3、结果写入到HDFS中,这个是最大瓶颈,HDFS的吞吐量是非常低的。
这里以线性回归来举例,LR是一种迭代的算法,所谓迭代,也即是多轮,且每轮都依赖于上一轮的结果。我们假设一个迭代需要一个MR Job,
这个job有几个非常耗时的操作,全部和HDFS有关:
1、读取数据,即便是SGD(随机梯度下降),也是需要读取全部的训练数据的。
2、写结果,因为每一轮为了给下一轮提供数据,每个Job都会把计算结果(梯度)返回给Driver而不是写入HDFS。
我们假设HDFS的吞吐量是本地磁盘的吞吐量的1/10,那么如果存在这么一种框架,它能让我们不读HDFS,而是直接读和写本地磁盘,那么每一轮迭代就只需要MR实现的1/10的时间。我们再假设如果这个框架能把数据全部放内存中,省去读取磁盘的,那么它就能更快,达到100倍速度于MR模式。
Spark针对对于MapReduce的几个约束,做出以下突破:
1、对于新的任务,Executor只需启动一条线程即可,这让job在启动方面变得很快,每个任务的启动负载相对于启动一个全新的JVM来说是非常轻的。
2、Spark在shuffle时其实也是要写本地磁盘的,不过它的reduce端是用内存中的HashMap做aggregate或reduce,相比较MapReduce的reduce端用磁盘来做merge,基于内存的方法会快很多。不过Spark在这点上有个缺陷,那么就是如果reduce端的内存不够,会报OOM错误,因此需要用户设定一个正确的reduce端的分区数。
3、Spark在执行一个action后,可以把结果放在内存或者本地磁盘中,而非HDFS上,鉴于内存和本地磁盘的吞吐量都高于HDFS,这种做法非常适合迭代逻辑。
Spark就是这么一种框架,它打破了MR框架的约束,让迭代任务变得非常地快。