2.1 数据处理

随着互联网的持续发展,我们可收集获取的数据规模不断增大,尽管数据的收集存储技术还在进步和日趋成熟,但是如何处理如此庞大的数据成为新的研究问题。在分布式系统出现之前,只有通过不断增加单个处理机的频率和性能缩短数据的处理时间,而分布式的提出则打破了这个传统的约束。所谓分布式,就是将一个复杂的问题切割成很多子任务,分布到多台机器上并行处理。在保证系统稳定性的同时,最大限度地提高系统的运行速度。

谷歌在2004年提出了最原始的分布式架构模型MapReduce,用于大规模的数据并行处理。MapReduce模型借鉴了函数式程序设计语言中的内置函数Map和Reduce,主要思想是将大规模数据处理作业拆分成多个可独立运行的Map任务,分布到多个处理机上运行,产生一定量的中间结果,再通过Reduce任务混洗合并产生最终的输出文件。作为第一代分布式架构,MapReduce已经比较好地考虑了数据存储、调度、通信、容错管理、负载均衡等问题,一定程度上降低了并行程序开发的难度,也为后来的分布式系统的开发打下了很好的基础。然而,它也存在很多不足:首先,为了保证较好的可扩展性,MapReduce的任务之间相互独立,互不干扰,所造成的后果是大量的中间结果需要通过网络进行传输,占用了网络资源,并且为了保证容错,所有中间结果都要存储到磁盘中,效率不高;同时,在MapReduce中只有等待所有的Map任务结束后,Reduce任务才能进行计算,异步性差,导致资源利用率低。

Spark作为新一代的大数据处理框架,以其先进的设计理念迅速成为热点。在处理迭代问题以及一些低延迟问题上,Spark性能要高于MapReduce。Spark在MapReduce的基础上进行了很多改进与优化,使得在处理如机器学习以及图算法等迭代问题时,Spark性能要优于MapReduce。Spark作为轻量、基于内存计算的分布式计算平台,采用了与MapReduce类似的编程模型,使用弹性分布式数据集对作业调度、数据操作和存取进行修改,并增加了更丰富的算子,使得Spark在处理迭代问题、交互问题以及低延时问题时有更高的效率。

本章首先介绍MapReduce的背景、具体的模型结构以及作业的调度策略,然后介绍Spark模型的具体思想以及与MapReduce的区别,接下来介绍弹性分布式数据集的概念以及基本操作。

2.1.1 MapReduce

MapReduce是一种编程模型,利用集群上的并行和分布式算法处理和产生大数据。MapReduce程序包括两个部分:第一个部分由Map过程组成,该过程执行过滤和排序,例如按产品的类别进行分类,每个分类形成一组;第二部分为reduce过程,该过程执行摘要操作,例如计算每组中产品的个数,生成分类的汇总。MapReduce系统也称为基础结构或框架,基于分布式服务器并行运行各种任务和管理系统各个部分之间的通信和数据传输,并提供冗余和容错协调处理过程。

MapReduce模型是一个特定的知识领域,应用(拆分—应用—组合)策略进行数据分析。这种策略的灵感来源于函数编程的常用功能map和reduce。MapReduce模型的Map和Reduce函数都是针对键值对(key,value)数据结构定义的。Map会在数据域中获取一对具有某种类型的数据,并经过数据结构的转换返回键值对列表:

Map()方法并行应用于输入数据集中的每一键值对,以k1为键;每次调用将产生一个成对列表list(k2,v2),以k2为键;之后从所有列表中收集具有相同键(k2)的所有键值对,并将它们分组在一起,为每个k2创建一个组(k2,list(v2));将其作为Reduce函数输入,然后将Reduce函数并行应用于每个组,生成值的聚合:

尽管允许一个调用返回多个键值对,但是每个Reduce调用通常会产生一个键值对或空返回,所有调用的返回将被收集为所需的结果列表list((k3,v3)),因此MapReduce模型将键值对的列表转换为键值对的另一个列表。

另外,为了更好地理解MapReduce模型的概念,通过示例讲解计算一组文本文件中每个单词的出现次数。在这个例子中,输入数据集是两个文档,包括文档1和文档2,所以数据集分每个文档为一个分割,总共2个分割,如图2-1所示。

图2-1 分割文件

将输入文档中的每一行生成键值对,其中行号为键,文本为值。Map阶段丢弃行号,并将输入行中的每个单词生成一个键值对,其中单词为键而1为值表示这个单词出现一次。Reduce阶段生成新的键值对,其中单词为键而值为这个单词个数汇总,根据上面显示的输入数据,示例Map和Reduce的工作进程如图2-2所示。

图2-2 Map和Reduce的工作进程

Map阶段的输出包含多个具有相同键的键值对,例如oats和eat出现两次,Reduce的工作就是对键值对的值进行聚合。如果在进入Reduce阶段之前加入洗牌(Shuffle)的过程,使得具有相同键的值合并为一个列表,例如("eat",(1,1)),则进行Reduce的输入实际上是键值对(key,value list)。因此,从Map输出到Reduce,然后到最终结果的完整过程如图2-3所示。

图2-3 Shuffle过程

为了实现MapReduce模型,如果只是实现了Map和Reduce的抽象概念,对于分布式系统是不够的,需要一种方法连接执行Map和Reduce阶段的流程。Apache Hadoop包含一个流行的、支持分布式洗牌的开源MapReduce实现。MapReduce库已经用许多编程语言编写,并具有不同的优化级别。但是,在Hadoop的MapReduce框架中,这两个功能与其原始形式发生了变化。MapReduce框架的主要贡献不是仅实现了的map()和reduce()方法,而是通过优化执行引擎实现了各种应用程序的可伸缩性和容错能力。因此,MapReduce的单线程实现通常不会比传统的非MapReduce实现快,通常只有在多处理器服务器系统上实现多线程处理才能看到其优势。优化的分布式洗牌操作可以降低网络通信成本,结合MapReduce框架的容错功能,使用此模型才可以发挥有利的作用。所以,对于优良的MapReduce算法而言,优化通信成本至关重要。

目前,MapReduce框架是一个用于在Hadoop集群中并行处理大型数据集的框架,使用Map和Reduce两步过程进行数据分析。作业(Job)配置提供Map和Reduce分析功能,Hadoop框架提供调度、分发和并行服务。在MapReduce中的顶级工作单元是作业,每个作业通常有一个Map和一个Reduce阶段,有时Reduce阶段可以省略。假设一个MapReduce的作业,它计算每个单词在一组文档中使用的次数。Map阶段对每个文档中的单词进行计数,然后Reduce阶段将每个文档数据聚合为跨越整个集合的单词计数。在Map阶段,输入数据被分割,以便在Hadoop集群中运行并行Map任务进行分析。默认情况下,MapReduce框架从Hadoop分布式文件系统(HDFS)获取输入数据。Reduce阶段将来自Map任务的结果作为输入送到Reduce任务。Reduce任务将数据整合到最终结果中。默认情况下,MapReduce框架将结果存储在HDFS中。

实际上,Spark也是基于Map和Reduce上的集群计算框架,但其最大的特点可以通过将数据保存在内存中,使Map和Reduce的运行速度比MapReduce快40多倍,并且可以交互式使用,以便以亚秒级的时间间隔查询大型数据集。随着企业开始将更多数据加载到Hadoop中,他们很快就想运行丰富的应用程序,MapReduce的单通批处理模型不能有效支持,特别是用户想要运行:

· 更复杂的递归算法,例如机器学习和图形处理中常见的迭代算法

· 更多交互式即席查询来探索数据

虽然这些应用程序最初看起来可能完全不同,但核心问题是多路径和交互式应用程序,这需要跨多个MapReduce步骤共享数据,例如来自用户的多个查询或迭代计算的多个步骤。但是,在MapReduce的并行操作之间共享数据的唯一方法是将其写入分布式文件系统,由于数据复制和磁盘I/O会增加大量开销。事实上,这种开销可能会占用MapReduce的通用机器学习算法运行时间的90%以上。

2.1.2 工作机制

现在可以通过比较MapReduce和Spark数据处理的方式,理解Spark的运行机制。大数据的处理方式包括两种:批处理和流处理。批处理对于大数据处理至关重要,用最简单的术语来说,批处理可以在一段时间内处理大量数据。在批处理中,首先收集数据,然后在以后的阶段中生成处理结果。批处理是处理大型静态数据集的有效方法。通常,我们对存档的数据集执行批处理。例如,计算一个国家的平均收入或评估过去十年中电子商务的变化。流处理是目前大数据处理的发展趋势,每小时需要的是处理速度和实时信息,这就是流处理所要做的。批处理对实时变化的业务需求不能做出快速的反应,所以流处理的需求迅速增长。

回顾Hadoop数据处理架构,其中的YARN基本上是一个批处理框架。当向YARN提交作业时,它会从集群读取数据,执行操作并将结果写回到集群,然后YARN再次读取更新的数据,执行下一个操作并将结果写回到群集中,以此类推。Spark执行类似的操作,但是它使用内存处理并优化了步骤。另外,Spark的GraphX组件允许用户查看与图和集合相同的数据,用户还可以使用RDD转换和连接图形。

Hadoop和Spark均提供容错能力,但是两者都有不同的方法。对于HDFS和YARN,主守护程序(分别为NameNode和ResourceManager)都检查从守护程序(分别为DataNode和NodeManager)的心跳。如果从守护程序发生故障,则主守护程序会将所有挂起和正在进行的操作重新计划到另一个从属。这种方法是有效的,但是它也可以显著增加单个故障操作的完成时间。Hadoop一般使用大量的、低成本的硬件组成集群,所以HDFS确保容错的另一种方法是在集群中复制数据。如上所述,RDD是Apache Spark的核心组件,为Spark提供容错能力,可以引用外部存储系统,如HDFS、HBase和共享文件系统中存在的任何数据集实现并行操作。Spark通过提供RDD的分布式存储框架解决计算过程中数据的缓存和传递。RDD可以将数据集持久存储在内存中,所以Spark的数据操作是基于内存的。Spark可以跟踪和记录从原始数据到最终结果的计算过程,如果RDD中的数据丢失,可以重新计算。RDD允许用户跨越查询将数据存储在内存中,并提供容错功能,而无须复制,这使RDD的读取和写入速度比典型的分布式文件系统快,所以,在RDD核心组件上构建的应用组件可以更快地运行。

Hadoop最适合的用例是分析存档数据。YARN允许并行处理大量数据。数据的一部分在不同的DataNode上并行处理,并从每个NodeManager收集结果。如果不需要即时结果。MapReduce框架是批处理的一种很好且经济的解决方案。Spark最适合的用例是实时大数据分析。实时数据分析意味着处理由实时事件流生成的数据,这些数据以每秒数百万个事件的速度进入,例如某些社交媒体的数据。Spark的优势在于,它能够支持数据流以及分布式处理。这是一个有用的组合,可提供近乎实时的数据处理。实时数据也可以在MapReduce上进行处理,但是MapReduce旨在对大量数据执行分布式批处理,这个特点使其实时处理速度远远不能满足Spark的要求。Spark声称处理数据的速度比MapReduce快100倍,如果基于磁盘,也要快10倍。

大多数图处理算法(如网页排名算法)需要对同一数据集执行多次迭代计算,这需要在迭代计算之间的消息传递机制。我们需要基于MapReduce框架进行编程,以处理对相同数据集的多次迭代。大致来说,它的工作步骤是先从磁盘读取数据,并在特定的迭代之后将结果写入HDFS,然后从HDFS读取数据以进行下一次迭代。这是非常低效的,因为它涉及读取和写入数据到磁盘,这涉及大量读写操作以及跨集群的数据复制,以实现容错能力。而且每个MapReduce迭代都具有很高的延迟,并且下一个迭代只能在之前的作业完全完成之后才能开始。同样,消息传递需要相邻节点的分数,以便评估特定节点的分数。这些计算需要来自其邻居的消息或跨作业多个阶段的数据,而MapReduce缺乏这种机制。为了满足对图处理算法的高效平台的需求,设计了诸如Pregel和GraphLab之类的不同图处理工具。这些工具快速且可扩展,但对于这些复杂的多阶段算法的创建和后续处理效率不高。Apache Spark的引入在很大程度上解决了这些问题。Spark包含一个称为GraphX的图计算库,可简化我们的工作。与传统的MapReduce程序相比,内存中的计算以及内置的图形支持将算法的性能提高1~2倍。Spark使用Netty和Akka的组合在整个执行程序中分发消息。图2-4给出了Spark的迭代操作示意,它将中间结果存储在分布式存储器中(内存),而不是存储在磁盘中,并使应用系统更快地运行。

图2-4 Spark的迭代操作

另外,几乎所有的机器学习算法都是基于迭代计算的工作机制。如前所述,迭代算法在MapReduce实现中涉及磁盘读写瓶颈。MapReduce使用的粗粒度任务(即任务级并行处理)对于迭代算法而言过于繁重。在分布式系统内核Mesos的帮助下,Spark会在每次迭代后缓存中间数据集,并在此缓存的数据集上运行多次迭代,从而减少磁盘读写,并有助于以容错的方式更快地运行算法。Spark有一个内置可扩展的机器学习库MLlib,其中包含高质量的算法,该算法利用迭代并产生比MapReduce使用时间更少的效果。Spark另一个功能是交互式分析界面,将Spark的运行结果立即提供给用户,无须集成开发工具和代码编译。此功能可以作为交互式探索数据的主要工具,也可以对正在开发的应用程序进行分步测试。下面的代码显示了一个Spark Shell,用户在其中加载一个文件,然后计算文件的行数。

代码2-1

如本示例所示,Spark可以从文件中读取和写入数据,然后在内存中缓存数据集,用户可以交互地执行各种各样的复杂计算,每行命令执行完即时返回结果。Spark提供了分别支持Scala、Python和R语言的交互界面启动程序。图2-5显示了Spark的交互式操作,如果用户在同一组数据上重复运行多次查询,则这组数据可以保存在内存中,便于后续的查询操作,数据只需要从磁盘中读取一次,这种运作机制可以获得更少的执行时间。

MapReduce作为第一代大数据分布式架构,让传统的大数据问题可以并行地在多台处理机上进行计算。而MapReduce之所以能够迅速成为大数据处理的主流计算平台,得力于其自动并行、自然伸缩、实现简单和容错性强等特性。但是,MapReduce并不适合处理迭代问题以及低延时问题,而Spark作为轻量、基于内存计算的分布式计算平台,采用了与MapReduce类似的编程模型,使用RDD抽象对作业调度、数据操作和存取进行修改,并增加了更丰富的算子,使得Spark在处理迭代问题、交互问题以及低延时问题时能有更高的效率。同样,Spark也有其不足:如数据规模过大或内存不足时,会出现性能降低、数据丢失需要进行重复计算等问题。总而言之,随着大数据领域的不断发展和完善,现有的大数据分析技术仍然有大量具有挑战性的问题需要深入研究,而作为大数据领域重要的两种分布式处理架构,MapReduce与Spark都有不可替代的地位和作用,它们彼此可以很好地互补。Hadoop将庞大的数据集置于大量低性能和低成本的硬件设备上,而Spark在内存中为需要它的数据集提供实时处理。当将Spark的能力(即高处理速度、高级分析和多重集成支持)与Hadoop在硬件上的低成本操作结合时,就可以提供最佳实践效果。

图2-5 交互式数据分析