Hadoop的诞生,不得不归功于Google从2003 年开始陆续发表的三大论文: GFS、MapReduce、BigTable。三篇跨时代的文章从理论上分别论述了在大规模商用计算机集群下工程实践需要解决的三大问题:
(1)GFS(Google File System)分布式文件系统,为上层应用提供了统一的文件读写接口,解决了TB级大文件的分块存储、读写和容错问题;
(2)MapReduce 分布式平台统一计算框架,提供了一套简洁的API,解决了数据在平台上如何进行计算的问题;
(3)BigTable 结构化数据库,支持列式存储语义,解决了大规模(PB)级结构化数据在分布式环境下的高效存取问题。
Google作为先行者公布了解决方案,打开了通往大数据世界的大门。可以毫不夸张的说,今天我们讨论的大规模数据处理系统都源自于这三篇文章的理论基础。虽然与大数据处理相关的技术和解决方案多如牛毛,仅在Apache上能找到的BigData相关的项目就有49个之多,但是原理和方法都是相通的,既然是要说清楚大数据处理这一课题所面临的问题和解决办法,无疑开源的Hadoop平台是最典型的学习案例。
本文的主旨就是从Hadoop生态环境中抽取出数据处理相关的两个子项目(Hadoop MapReduce、Spark)进行分析,试图说清楚目前主流的大数据处理技术的思想,并从实际应用的角度来看大数据处理技术能解决哪些问题。本文一部分摘自Apache官网上对具体技术的解释文档(源自全球开发者的贡献,大部分由项目的创始人亲自编写),一部分翻译自《Streaming System》(探讨流式数据处理方向难得一见的深度书籍,强烈推荐),另外一部分源自实际使用中的总结。话不多说,我们开始吧!
(一)MapReduce
可以说Hadoop生态下的MapReduce和Google提出的MapReduce没有本质上的区别。2003年MapReduce问世之前,谷歌的工程师正在构建各种定制化系统,以解决互联网时代下大规模数据处理难题。当他们这样尝试去解决这些问题时候,发现有三个难以逾越的坎儿:
(1)数据处理很难:只要是数据科学家或者工程师都很清楚。问题在于怎样从原始的大量操作型数据中挖掘出对企业有价值的信息,当时的处理方式是针对每个需求开发定制化的解决方案,既费时也费力;
(2)可伸缩性很难:本来从大规模数据集中挖掘出有价值的数据就已经很难了,要让系统适应数据规模,通过横向扩展硬件来提高系统的性能更加困难;
(3)容错很难:在前两个问题的基础之上,如果还要想办法在一批廉价机器构建的分布式集群上可容错地、准确地方式挖掘数据价值,那真是难于上青天了。
在多种应用场景中都尝试解决了上述三个问题之后,Google 的工程师们开始注意到各自构建的定制化系统之间颇有相似之处。最终,Google 工程师悟出来一个道理: 如果他们能够构建一个可以解决上述问题二和问题三的框架,那么工程师就将可以完全放下问题二和三,从而集中精力解决每个业务都需要解决的问题一。于是,MapReduce 框架应运而生。
MapReduce 的基本思想是提供一套非常简洁的数据处理 API,这套 API 来自于函数式编程领域的两个非常易于理解的操作:map 和 reduce(如下图所示)。使用该 API 构建的底层数据流将在这套分布式系统框架上执行,框架负责处理所有繁琐的可扩展性和容错性问题。可扩展性和容错性问题对于分布式底层工程师来说无疑是非常有挑战的课题,但对于我们普通工程师而言,无异于是灾难。
在上图中,我们将处理过程分解为六个离散阶段(MapRead,Map,MapWrite,ReduceRead,Reduce,ReduceWrite)作为对于流或者表进行分析的几个步骤。我们可以看到,整体上 Map 和 Reduce 阶段之间差异其实也不大 ; 更高层次来看,他们都做了以下事情:
(1)从表中读取数据,并转换为数据流。(即 MapRead、ReduceRead)
(2)针对上述数据流,将用户编写业务处理代码应用于上述数据流,转换并形成新的一个数据流。
(3)将上述转换后的流根据某些规则分组,并写出到表中。(即 MapWrite、 ReduceWrite)
Hadoop 于 2005 年问世,当时 Doug Cutting 和 Mike Cafarella 认为 MapReduce 论文中的想法太棒了,他们在构建 Nutch webcrawler 的分布式版本正好需要这套分布式理论基础。在这之前,他们已经实现了自己版本的 GFS(Google 分布式文件系统,最初称为 Nutch 分布式文件系统的 NDFS,后来改名为 HDFS 或 Hadoop 分布式文件系统)。因此下一步,自然而然的,基于 HDFS 之上添加 MapReduce 计算层。他们称 MapReduce 这一层为 Hadoop。
(二) Spark
Spark 在 2009 年左右诞生于加州大学伯克利分校的著名 AMP Lab。最初推动 Spark 成名的原因是它能够在内存执行大量的计算工作,直到作业的最后一步才写入磁盘。工程师通过弹性分布式数据集(RDD)理念实现了这一目标,在底层 Pipeline 中能够获取每个阶段数据结果的所有派生关系,并且允许在机器故障时根据需要重新计算中间结果,当然,这些都基于一些假设:
a)输入是总是可重放的(对于官网定义的 RDDs automatically recover from node failures 的解释,这里的重放指由于集群中计算节点失效导致RDD数据丢失时,会根据RDD的计算方法重新算得一份);
b)计算是确定性的。
对于许多案例来说,这些先决条件是成立的,用户也确实在 Spark 上享受到了巨大的性能提升。从那时起,Spark 逐渐建立起其作为 Hadoop 事实上的继任产品定位,用于取代MapReduce处理框架,毕竟后者在数据处理效率上确实不尽人意。
在Spark创建几年后,当时 AMP Lab 的研究生 Tathagata Das 开始意识到:嘿,我们有这个快速的批处理引擎,如果我们将多个批次的任务串接起来,用它能否来处理流数据?于是乎,Spark Streaming 诞生了。
在上图中,Spark-Streaming是整个处理的核心,它支持多种数据来源,以及输出。
关于 Spark Streaming 的真正精彩之处在于:强大的批处理引擎解决了太多底层麻烦的问题,如果基于此构建流式处理引擎则整个流处理系统将简单很多。更进一步,Spark Streaming提供了At Least Once的流处理一致性语义(其他两种分别为:At Most Once、Exact Once)。换句话说,在固定的使用场景下,你可以直接使用 Spark Streaming 即可满足数据一致性需求。
这里的一个主要问题是“固定的使用场景”部分。早期版本的 Spark Streaming(1.x 版本)的一大缺点是它仅支持“基于时间窗口”的流处理场景。因此,任何需要使用事件触发,需要处理延迟数据等等案例都无法让用户使用“ Spark 开箱即用”解决业务问题。这意味着 Spark Streaming 最适合于有序数据或与事件无关的计算。而且,在处理如今常见的大规模、以用户为中心的数据集时,这些先决条件看上去并不是那么常见。
*如果想了解有关原始 Spark 1.x 架构细节的更多信息,我强烈推荐 Matei Zaharia 关于该主题的论文《 “An Architecture for Fast and General Data Processing on Large Clusters》