1.1 大数据处理架构演进历程
谷歌发表的三篇划时代论文(分别介绍MapReduce、GFS和BigTable),特别是介绍MapReduce的那篇论文,开启了大规模数据处理波澜壮阔的发展历程。一篇篇论文和那些大数据从业者耳熟能详的大数据处理架构,是这个历程中的重要里程碑,图1-1所示为主流大数据处理架构的发展历程。
图1-1 主流大数据处理架构的发展历程
2003年,谷歌的工程师便开始构建各种定制化数据处理系统,以解决大规模数据处理的几大难题:大规模数据处理特别困难(Data Processing is hard),这里的难有多个方面,仅仅是在大规模数据上构建一个处理程序也不是一件容易的事情;保证可伸缩性很难(Scalability is hard),让处理程序在不同规模的集群上运行,或者更进一步,让程序根据计算资源状况自动调度执行,也不是一件容易的事情;容错很难(Fault-tolerance is hard),让处理程序在由廉价机器组成的集群上可靠地运行,更不是一件容易的事情。这些困难促使 MapReduce(不是 Hadoop中的MapReduce)诞生。MapReduce将处理抽象成Map+Shuffle+Reduce的过程,这种抽象对大数据处理理论变革有着深远的影响。
以计算词频为例,MapReduce将输入(Input)文本以行为单位分片(Split),每个Map任务将分片中的每个词映射为键值对的形式(Dear,1),Shuffle将相同键的记录组合在一起,最后由Reduce任务计算词频并输出(Output)结果,图1-2描述了一个有3个Map和3个Reduce的词频计算过程。
图1-2 基于MapReduce计算词频的过程
笔者有一段相似的架构经历,能够帮助读者更好地理解是什么驱动谷歌的工程师开发MapReduce这个通用框架。驱动笔者开发一个定制化数据处理程序的想法主要来自业务需求,也有 MapReduce 思想的启发。当时,笔者就职的公司有TB级的短文本数据,笔者需要将这些文本的一些相邻行合并成一条记录,再对这些记录进行聚合操作,并在这之上构建一个用于语义分析的应用。出于保密要求,这些数据被分批归集到公司内网的一台 x86服务器上,语义分析程序也运行在这台内网机器上。笔者有两套方案,其中一套方案使用Hadoop,但是由于只有两台物理机器,而且用Hadoop有点“大炮打蚊子”的感觉,加之因着迷于Linux内核之美而“继承”下的“一言不合便动手造轮子”的理念,笔者决定采用另一套方案:使用Java语言自己动手构建一个简易的、定制化的多线程数据处理框架(类MapReduce数据处理框架),如图1-3所示。
其中,Reader用于并行读取数据;Dealer用于实现可级联的数据处理逻辑,如先计算记录总数,再过滤非目标记录,最后分词并计算语义标签;Writer将Dealer处理的最终结果以配置的格式写入输出文件。
图1-3 类MapReduce数据处理框架
多线程并行处理将程序运行速度提高了好几个量级。尽管如此,这段经历也令笔者回味深长:
(1)语义分析应用程序和底层组件间耦合得太紧,以至于这套软件只能由笔者维护。因为承担这个任务的部门的其他同事都是做数据分析的,没有软件开发工作经验。
(2)语义分析训练通常是相当耗时的,没有功能更强大的框架支持,手工操作的时间成本比较高。
这段经历让笔者深刻领悟到MapReduce框架的深思熟虑。
2004年,Doug Cutting和Mike Cafarella在构建Nutch时受到谷歌公司发表的MapReduce论文的启发,实现了开源版本的MapReduce,即Hadoop。此后,Pig、Hive、HBase等工具不断涌现,Hadoop批处理生态系统蓬勃发展,也让人们再次领教了开源的力量,图1-4展示了Hadoop生态系统。
图1-4 Hadoop生态系统
批处理(batch)的概念由来已久。在操作系统理论中,批处理是指用户将一批作业提交给操作系统后就不再干预,由操作系统控制它们自动运行。这种操作系统被称为批处理操作系统,它是为了提高CPU的利用率而提出的一种操作系统。例如,在DOS和Windows系统中,我们可以在扩展名为.bat 的脚本文件中顺序定义一系列操作命令,让操作系统自动运行这些命令。
在数据处理理论中有对应的批处理系统。批处理系统的核心功能是在大容量静态数据集上运行预定义功能的、可预期完成时间的计算任务。这里的静态是指数据集是有界的,是数据集的时间属性。
流处理(streaming)系统则是构建在无界数据集之上的、可提供实时计算的另一类数据处理系统。
经过一段时间的应用实践,MapReduce的缺陷也逐渐暴露,最让人诟病的是Map+Shuffle+Reduce编程模型导致计算作业效率低下。为此,2007年,谷歌发起了Flume项目。起初,Flume只有Java版本,因此也被称为Flume Java(这里所说的Flume和Apache的Flume不同)。Flume将数据处理过程抽象成计算图(有向无环图),数据处理逻辑被编译成 Map+Shuffle+Reduce 的组合,并加入物理执行计划优化,而不是简单地将Map+Shuffle+Reduce串联。
Flume引入的管道(Pipeline)、动态负载均衡(谷歌内部称为液态分片)和流语义思想成为大数据处理技术变革的宝贵理论财富。
产生于处理推特信息流的流式数据处理框架 Storm 以牺牲强一致性换取实时性,并在一些场景下取得了成功。为了让数据处理程序兼备强一致性和实时性,工程师们将强实时性的 Storm 和强一致性的 Hadoop 批处理系统融合在一起,即Lambda架构。其中,Storm负责实时生成近似结果,Hadoop负责计算最终精准结果。Lambda架构需要部署两套队列集群,数据要持久化存放两份,这会导致数据冗余,增加系统维护成本。Lambda架构示意图,如图1-5所示。
图1-5 Lambda架构示意图
MapReduce模型严重依赖分布式文件系统,如Map将计算结果临时写入文件系统,而Shuffle从文件系统中读入该结果,这往往会产生较大的计算性能损耗,因此基于内存的计算是另一个选择,这就是Spark成功的秘诀。此外,Spark还支持流式数据处理,即Spark Streaming,其原理是将多个微批处理任务串接起来构建流式数据处理任务。但是这种采用微批重复运行的机制牺牲了低延迟和高吞吐的优势,引发了 Spark Streaming 是不是真正流式数据处理引擎的争议。Spark Streaming流式数据处理任务的架构方案,如图1-6所示。
图1-6 Spark Streaming流式数据处理任务的架构方案
这期间,流式数据处理继续发展,出现了 MillWheel、Kafka 和 DataFlow。exactly-once语义、数据源端的持久化和可重放、动态表理论,以及时间、窗口、水印、触发器等流式数据处理核心理论的提出,加快了流式数据处理框架的发展步伐。
作为流式数据处理中容错的解决方案之一的轻量级快照机制,借助上述流式数据处理相关理论,以及开源的旺盛生命力,Flink 于 2015 年迅速登上实时数据处理的舞台,并将推动大数据发展新的浪潮。正是在这种背景下,笔者决定深入Flink实现底层,为读者呈现其中的智慧之光。Flink架构,如图1-7所示。
图1-7 Flink架构