Hadoop 和 Spark 都是目前支流的大数据框架,然而随着 Spark 在速度和易用性方面体现出的劣势,一些国内外专家逐步推崇 Spark 技术,并且认为 Spark 才是大数据的将来。本文将会浅析 Hadoop 生态的倒退历程及其中局部组件的技术原理,最终就 Hadoop 是否会被 Spark 取代给出论断。
一、Hadoop 的外围组件
在对 Hadoop 外围组件进行介绍之前,咱们须要先理解 Hadoop 解决了什么问题。Hadoop 次要就是解决了大数据(大到一台计算机无法进行存储,一台计算机无法在要求的工夫内进行解决)的牢靠存储和解决。
Hadoop 的外围组件次要有三个,别离是:HDFS、YARN 和 MapReduce。HDFS 是是 google 三大论文之一的 GFS 的开源实现,是一个高度容错性的零碎,适宜部署在便宜的机器上的,适宜存储海量数据的分布式文件系统。而 YARN 是 Hadoop 的资源管理器,能够视为一个分布式的操作系统平台。相比于 HDFS 和 YARN,MapReduce 能够说是 Hadoop 的外围组件,以下会就 MapReduce 进行重点探讨。
MapReduce,通过简略的 Mapper 和 Reducer 的形象提供一个编程模型,能够在一个由几十台上百台的 PC 组成的不牢靠集群上并发地,分布式地解决大量的数据集,而把并发、分布式(如机器间通信)和故障复原等计算细节暗藏起来。而 Mapper 和 Reducer 的形象,又是各种各样的简单数据处理都能够合成为的根本元素。这样,简单的数据处理能够合成为由多个 Job(蕴含一个 Mapper 和一个 Reducer)组成的有向无环图(DAG), 而后每个 Mapper 和 Reducer 放到 Hadoop 集群上执行,就能够得出后果。
在 MapReduce 中,Shuffle 是一个十分重要的过程,正是有了看不见的 Shuffle 过程,才能够使在 MapReduce 之上写数据处理的开发者齐全感知不到分布式和并发的存在。
狭义的 Shuffle 是指图中在 Map 和 Reuce 之间的一系列过程。
二、Hadoop 的局限和改良
只管 Hadoop 提供了解决海量数据的能力,然而 Hadoop 的外围组件——MapReduce 的应用问题还是始终困扰着 Hadoop 的倒退,MapReduce 的局限次要能够总结为以下几点:
- 抽象层次低,须要手工编写代码来实现,应用上难以上手
- 只提供两个操作,Map 和 Reduce,表达力欠缺
- 一个 Job 只有 Map 和 Reduce 两个阶段(Phase),简单的计算须要大量的 Job 实现,Job 之间的依赖关系是由开发者本人治理的
- 解决逻辑暗藏在代码细节中,没有整体逻辑
- 两头后果也放在 HDFS 文件系统中
- ReduceTask 须要期待所有 MapTask 都实现后才能够开始
- 时延高,只实用 Batch 数据处理,对于交互式数据处理,实时数据处理的反对不够
- 对于迭代式数据处理性能比拟差
比如说,用 MapReduce 实现两个表的 Join 都是一个很有技巧性的过程,如下图所示:
因而,在 Hadoop 推出之后,呈现了很多相干的技术对其中的局限进行改良,如 Pig,Cascading,JAQL,OOzie,Tez,Spark 等,上面就对一些重要技术进行重点的探讨。
1.Apache Pig
Apache Pig 是 Hadoop 框架中的一部分,Pig 提供类 SQL 语言(Pig Latin)通过 MapReduce 来解决大规模半结构化数据。而 Pig Latin 是更高级的过程语言,通过将 MapReduce 中的设计模式形象为操作,如 Filter,GroupBy,Join,OrderBy,由这些操作组成 有向无环图(DAG)。例如如下程序就形容了数据处理的整个过程。
visits = load‘/data/visits’as (user, url, time);
gVisits = group visits by url;
visitCounts = foreach gVisits generate url, count(visits);
urlInfo = load‘/data/urlInfo’as (url, category, pRank);
visitCounts = join visitCounts by url, urlInfo by url;
gCategories = group visitCounts by category;
topUrls = foreach gCategories generate top(visitCounts,10);
store topUrls into‘/data/topUrls’;
而 Pig Latin 又是通过编译为 MapReduce,在 Hadoop 集群上执行的。上述程序被编译成 MapReduce 时,会产生如下图所示的 Map 和 Reduce:
Apache Pig 解决了 MapReduce 存在的大量手写代码,语义暗藏,提供操作品种少的问题。相似的我的项目还有 Cascading,JAQL 等。
2.Apache Tez
Apache Tez,Tez 是 HortonWorks 的 Stinger Initiative 的的一部分。作为执行引擎,Tez 也提供了 有向无环图(DAG),DAG 由顶点(Vertex)和边(Edge)组成,Edge 是对数据的挪动的形象,提供了 One-To-One,BroadCast,和 Scatter-Gather 三种类型,只有 Scatter-Gather 才须要进行 Shuffle。
以如下 SQL 为例:
SELECT a.state, COUNT(*),
AVERAGE(c.price)
FROM a
JOIN b ON (a.id = b.id)
JOIN c ON (a.itemId = c.itemId)
GROUP BY a.state
图中蓝色方块示意 Map,绿色方块示意 Reduce,云状示意写屏障(write barrier,一种内核机制,能够了解为长久的写),Tez 的优化次要体现在:
去除了间断两个作业之间的写屏障
去除了每个工作流中多余的 Map 阶段(Stage)
通过提供 DAG 语义和操作,提供了整体的逻辑,通过缩小不必要的操作,Tez 晋升了数据处理的执行性能。
3.Apache Spark
Apache Spark 是一个新兴的大数据处理的引擎,次要特点是提供了一个集群的分布式内存形象,以反对须要工作集的利用。
这个形象就是 RDD(Resilient Distributed Dataset),RDD 就是一个不可变的带分区的记录汇合,RDD 也是 Spark 中的编程模型。Spark 提供了 RDD 上的两类操作,转换和动作。转换是用来定义一个新的 RDD,包含 map, flatMap, filter, union, sample, join, groupByKey, cogroup, ReduceByKey, cros, sortByKey, mapValues 等,动作是返回一个后果,包含 collect, reduce, count, save, lookupKey。
Spark 的 API 非常简单易用,Spark 的 WordCount 的示例如下所示:
val spark = new SparkContext(master, appName, [sparkHome], [jars])
val file = spark.textFile("hdfs://...")
val counts = file.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://...")
其中的 file 是依据 HDFS 上的文件创建的 RDD,前面的 flatMap,map,reduceByKe 都创立出一个新的 RDD,一个简短的程序就可能执行很多个转换和动作。
在 Spark 中,所有 RDD 的转换都是是 惰性求值 的。RDD 的转换操作会生成新的 RDD,新的 RDD 的数据依赖于原来的 RDD 的数据,每个 RDD 又蕴含多个分区。那么一段程序实际上就结构了一个由相互依赖的多个 RDD 组成的有向无环图(DAG)。并通过在 RDD 上执行动作将这个有向无环图作为一个 Job 提交给 Spark 执行。
例如,下面的 WordCount 程序就会生成如下的 DAG:
scala> counts.toDebugString
res0: String =
MapPartitionsRDD[7] at reduceByKey at <console>:14 (1 partitions)
ShuffledRDD[6] at reduceByKey at <console>:14 (1 partitions)
MapPartitionsRDD[5] at reduceByKey at <console>:14 (1 partitions)
MappedRDD[4] at map at <console>:14 (1 partitions)
FlatMappedRDD[3] at flatMap at <console>:14 (1 partitions)
MappedRDD[1] at textFile at <console>:12 (1 partitions)
HadoopRDD[0] at textFile at <console>:12 (1 partitions)
Spark 对于有向无环图 Job 进行调度,确定 阶段(Stage),分区(Partition),流水线(Pipeline),工作(Task)和 缓存(Cache),进行优化,并在 Spark 集群上运行 Job。RDD 之间的依赖分为 宽依赖 (依赖多个分区)和 窄依赖(只依赖一个分区),在确定阶段时,须要依据宽依赖划分阶段。依据分区划分工作。
Spark 反对故障复原的形式也不同,提供两种形式,Linage,通过数据的血缘关系,再执行一遍后面的解决,Checkpoint,将数据集存储到长久存储中。
Spark 为 迭代式数据处理 提供更好的反对。每次迭代的数据能够保留在内存中,而不是写入文件。
Spark 的性能相比 Hadoop 有很大晋升,2014 年 10 月,Spark 实现了一个 Daytona Gray 类别的 Sort Benchmark 测试,排序齐全是在磁盘上进行的,与 Hadoop 之前的测试的比照后果如表格所示:
从表格中能够看出排序 100TB 的数据(1 万亿条数据),Spark 只用了 Hadoop 所用 1 /10 的计算资源,耗时只有 Hadoop 的 1 /3。
Spark 的劣势不仅体现在性能晋升上的,Spark 框架为批处理(Spark Core),交互式(Spark SQL),流式(Spark Streaming),机器学习(MLlib),图计算(GraphX)提供一个对立的数据处理平台,这绝对于应用 Hadoop 有很大劣势。
特地是在有些状况下,你须要进行一些 ETL 工作,而后训练一个机器学习的模型,最初进行一些查问,如果是应用 Spark,你能够在一段程序中将这三局部的逻辑实现造成一个大的有向无环图(DAG),而且 Spark 会对大的有向无环图进行整体优化。
例如上面的程序:
val points = sqlContext.sql(“SELECT latitude, longitude FROM historic_tweets”)
val model = KMeans.train(points, 10)
sc.twitterStream(...) .map(t => (model.closestCenter(t.location), 1)) .reduceByWindow(“5s”, _ + _)
这段程序的第一行是用 Spark SQL 查寻出了一些点,第二行是用 MLlib 中的 K -means 算法应用这些点训练了一个模型,第三行是用 Spark Streaming 解决流中的音讯,应用了训练好的模型。
三、总结
咱们能够借助于逻辑电路来了解 MapReduce 和 Spark。如果说 MapReduce 是公认的分布式数据处理的低层次形象,相似逻辑门电路中的与门,或门和非门,那么 Spark 的 RDD 就是分布式大数据处理的高层次形象,相似逻辑电路中的编码器或译码器等。
RDD 就是一个分布式的数据汇合(Collection),对这个汇合的任何操作都能够像函数式编程中操作内存中的汇合一样直观、简便,但汇合操作的实现却是在后盾分解成一系列 Task 发送到几十台上百台服务器组成的集群上实现的。最近新推出的大数据处理框架 Apache Flink 也应用数据集(Data Set)和其上的操作作为编程模型的。
由 RDD 组成的有向无环图(DAG)的执行是调度程序将其生成物理打算并进行优化,而后在 Spark 集群上执行的。Spark 还提供了一个相似于 MapReduce 的执行引擎,该引擎更多地应用内存,而不是磁盘,失去了更好的执行性能。
基于此,Spark 针对于 Hadoop 的一些局限进行了解决:
- 抽象层次低,须要手工编写代码来实现,应用上难以上手
=> 基于 RDD 的形象,实数据处理逻辑的代码十分简短 - 只提供两个操作,Map 和 Reduce,表达力欠缺
=> 提供很多转换和动作,很多基本操作如 Join,GroupBy 曾经在 RDD 转换和动作中实现 - 一个 Job 只有 Map 和 Reduce 两个阶段(Phase),简单的计算须要大量的 Job 实现,Job 之间的依赖关系是由开发者本人治理的
=> 一个 Job 能够蕴含 RDD 的多个转换操作,在调度时能够生成多个阶段(Stage),而且如果多个 map 操作的 RDD 的分区不变,是能够放在同一个 Task 中进行 - 解决逻辑暗藏在代码细节中,没有整体逻辑
=> 在 Scala 中,通过匿名函数和高阶函数,RDD 的转换反对流式 API,能够提供解决逻辑的整体视图。代码不蕴含具体操作的实现细节,逻辑更清晰 - 两头后果也放在 HDFS 文件系统中
=> 两头后果放在内存中,内存放不下了会写入本地磁盘,而不是 HDFS - ReduceTask 须要期待所有 MapTask 都实现后才能够开始
=> 分区雷同的转换形成流水线放在一个 Task 中运行,分区不同的转换须要 Shuffle,被划分到不同的 Stage 中,须要期待后面的 Stage 实现后才能够开始 - 时延高,只实用 Batch 数据处理,对于交互式数据处理,实时数据处理的反对不够
=> 通过将流拆成小的 batch 提供 Discretized Stream 解决流数据 - 对于迭代式数据处理性能比拟差
=> 通过在内存中缓存数据,进步迭代式计算的性能
因而,Hadoop MapReduce 会被新一代的大数据处理平台代替是技术倒退的趋势,而在新一代的大数据处理平台中,Spark 目前失去了最宽泛的认可和反对。
最初,咱们通过 Lambda Architecture 的一个案例作为完结和补充,Lambda Architecture 是一个大数据处理平台的参考模型,如下图所示:
其中蕴含 3 层,Batch Layer,Speed Layer 和 Serving Layer,因为 Batch Layer 和 Speed Layer 的数据处理逻辑是统一的,如果用 Hadoop 作为 Batch Layer,而用 Storm 作为 Speed Layer,你须要保护两份应用不同技术的代码。
而 Spark 能够作为 Lambda Architecture 一体化的解决方案, 大抵如下:
Batch Layer,HDFS+Spark Core,将实时的增量数据追加到 HDFS 中,应用 Spark Core 批量解决全量数据,生成全量数据的视图
Speed Layer,Spark Streaming 来解决实时的增量数据,以较低的时延生成实时数据的视图
Serving Layer,HDFS+Spark SQL(兴许还有 BlinkDB),存储 Batch Layer 和 Speed Layer 输入的视图,提供低时延的即席查问性能,将批量数据的视图与实时数据的视图合并
通过这个案例,咱们也能够再一次重申上述论断,Spark 是能够取代 MapReduce 从而成为 Hadoop 零碎中不可或缺的一部分,然而无奈取代 Hadoop 生态。