Google 早期三驾马车之 MapReduce 论文学习与研读

前言

Google,作为全球最大的搜索引擎公司,其伟大之处不仅在于建立了一个强大的搜索引擎,还在于它创造了3项革命性的技术,即:GFS、MapReduce 和 BigTable。作为 Google 早期三驾马车,这三项革命性的技术不仅在大数据领域广为人知,更直接或间接性的推动了大数据、云计算、乃至如今火爆的人工智能领域的发展。

2004年12月5日,Google 在美国旧金山召开的第6届操作系统设计与实现研讨会(Operating Systems Design and Implementation,OSDI)上,发表了论文《MapReduce: Simplified Data Processing on Large Clusters》(MapReduce:超大集群的简单数据处理),向全世界介绍了 MapReduce 系统的编程模式、实现、技巧、性能和经验。基于 MapReduce 编写的程序是在成千上万的普通PC机上被并行分布式自动执行的。它将所有服务器中的处理器有效地利用起来计算保存在谷歌文件系统中的海量数据并得到想要的结果。

关于 GFS 相关介绍与论文研读:查看 GFS

关于 BigTable 相关介绍与论文研读:查看 BigTable

随着这3篇重量级论文的发表,基于这3项技术的衍生技术与开源产品如雨后春笋般涌现(Hadoop 就是其中的一个)。至此,Google 以一种独特的方式,推动了大数据处理、云计算等技术的发展。

注:在后 Hadoop 时代,Google 推出新三驾马车 —— Caffeine、Pregel、Dremel

本文学习与研读最初的 MapReduce 论文,中英双文版见:中英双文版

总览

MapReduce 论文总览如图所示,主要包含八个部分:介绍、编程模型、实现、技巧、性能、经验、相关工作、结束语。

一、介绍

        定义:MapReduce 是一个编程模型,处理和生成超大数据集的算法模型的相关实现。
        设计初衷:使用由成百上千的机器组成的集群来处理超大规模的数据。
        解决问题:如何分割输入数据,在大量计算机组成的集群上的调度,集群中计算机的错误处理,管理集群中计算机之间必要的通信。
        原理:Map 函数处理一个基于 key/value pair (k/v 数据对)的数据集合,输出中间的基于 key/value pair 的数据集合;
然后再创建一个 Reduce 函数用来合并所有的具有相同中间 key 值的中间 value 值。

二、编程模型

        例子:统计一个大的文档集合中每个单词出现的次数。
        类型:Map(k1,v1) ->list(k2,v2)       Reduce(k2,list(v2)) ->list(v2)
        输入的 key 和 value 值与输出的 key 和 value 值在类型上推导的域不同。中间 key 和 value 值与输出 key 和 value 值在类型上推导的域相同。
        更多的例子:分布式的 Grep、计算 URL 访问频率、每个主机的检索词向量、倒排索引、分布式排序

三、实现

        1. 执行概括
        概述:将 Map 调用的输入数据自动分割为 M 个数据片段的集合,Map 调用被分布到多台机器上执行。
输入的数据片段能够在不同的机器上并行处理。
        流程图如下:

        执行过程
        1. 用户程序首先调用 MapReduce 库将输入文件分成 M 个数据片段,每个数据片段的大小一般从 16MB 到 64MB。然后用户程序在集群中创建大量程序副本。

        2. 副本程序中除了一个 master,其他都是 worker 程序,并由 master 分配 M 个 Map 任务和 R 个 Reduce 任务。master 将一个 Map 任务或 Reduce 任务分配给一个空闲的 worker。

        3. 被分配了 map 任务的 worker 程序读取相关的输入数据片段,解析 key/value pair,然后将结果传递给用户自定义的 Map 函数,生成并输出中间 key/value pair,并缓存在内存中。

        4. 缓存中的 key/value pair 通过分区函数分成 R 个区域(对应 R 个 Reduce 任务),之后周期性的写入到本地磁盘上。缓存的 key/value pair 在本地磁盘上的存储位置将被回传给 master,由 master 负责把这些存储位置再传送给 Reduce worker。

        5. 当 Reduce worker 程序接收到 master 程序发来的数据存储位置信息后,使用 RPC 从 Map worker 所在主机的磁盘上读取这些缓存数据。当 Reduce worker 读取了所有的中间数据后,通过对 key 进行排序后使得具有相同 key 值的数据聚合在一起。由于许多不同的 key 值会映射到相同的 Reduce 任务上,因此必须进行排序。如果中间数据太大无法在内存中完成排序,那么就要在外部进行排序。

        6. Reduce worker 程序遍历排序后的中间数据,对于每一个唯一的中间 key 值,Reduce worker 程序将这个 key 值和它相关的中间 value 值的集合传递给用户自定义的 Reduce 函数。Reduce 函数的输出被追加到所属分区的输出文件。

        7. 当所有的 Map 和 Reduce 任务都完成之后,master 唤醒用户程序。在这个时候,在用户程序里的对 MapReduce 调用才返回。

        8. 成功完成任务之后,MapReduce 的输出存放在 R 个输出文件中(对应每个 Reduce 任务产生一个输出文件,文件名由用户指定)。一般情况下,用户不需要将这 R 个输出文件合并成一个文件,他们经常把这些文件作为另外一个 MapReduce 的输入,或者在另外一个可以处理多个分割文件的分布式应用中使用。 

         2. Master 数据结构
         Master 数据结构存储每一个 Map 和 Reduce 任务的状态(空闲、工作中或完成),以及 Worker 机器(非空闲任务的机器)的标识。Master 就像一个数据管道,中间文件存储区域的位置信息通过这个管道从 Map 传递到 Reduce,对于每个已经完成的 Map 任务,master 存储了 Map 任务产生的 R 个中间文件存储区域的大小和位置的更新信息,这些信息被逐步递增的推送给那些正在工作的 Reduce 任务。
         3. 容错
            a. worker 故障
                I. 失效判定:master 周期性的 ping 每个 worker,在一个约定的时间范围内没有收到 worker 返回的信息,master 将把这个 worker 标记为失效。
                II. 故障处理:
                    正在运行:正在运行的 Map 或 Reduce 任务也将被重新置为空闲状态,等待重新调度
                    已完成:已完成的  Map 任务被重设为初始的空闲状态,被安排给其他的 worker 重新执行;已经完成的 Reduce 任务的输出存储在全局文件系统上,因此不需要再次执行。
            b. master 失败
                一个简单的解决办法是让 master 周期性的将数据结构写入磁盘,即检查点(checkpoint)。
如果这个 master 任务失效了,可以从最后一个检查点(checkpoint)开始启动另一个 master 进程。
现在的实现是如果 master 失效,就中止 MapReduce 运算。客户可以检查到这个状态,并且可以根据需要重新执行 MapReduce 操作。
            c. 在失效方面的处理机制
                失效处理机制等价于一个顺序的执行的操作,Map 和 Reduce 任务的输出、重命名文件输出均是原子性的。
        4. 存储位置
            尽量把输入数据(由 GFS 管理)存储在集群中机器的本地磁盘上来节省网络带宽;
            尽量将一个 Map 任务调度在包含相关输入数据拷贝的机器上执行;
            master 将尝试在保存有输入数据拷贝的机器附近的机器上执行 Map 任务。
        5. 任务粒度
            失效机器上执行的大量 Map 任务都可以分布到所有其他的 worker 机器上去执行。
            我们把 R 值设置为我们想使用的 worker 机器数量的小的倍数。通常会用这样的比例来执行 MapReduce:M=200000,R=5000,使用 2000 台 worker 机器。
        6. 备用任务
            当一个 MapReduce 操作接近完成的时候,master 调度备用(backup)任务进程来执行剩下的、处于处理中状态(in-progress)的任务。无论是最初的执行进程、还是备用(backup)任务进程完成了任务,我们都把这个任务标记成为已经完成。

四、技巧

        1. 分区函数
            在中间 key 上使用分区函数来对数据进行分区,之后再输入到后续任务执行进程。一个缺省的分区函数是使用 hash 方法(比如,hash(key) mod R)进行分区。
            hash 方法能产生非常平衡的分区。“hash(Hostname(urlkey)) mod R”作为分区函数就可以把所有来自同一个主机的 URLs 保存在同一个输出文件中。
        2. 顺序保证
            确保在给定的分区中,中间 key/value pair 数据的处理顺序是按照 key 值增量顺序处理的。这样的顺序保证对每个分区生成一个有序的输出文件
       3. Combiner 函数
            某些情况下,Map 函数产生的中间 key 值的重复数据会占很大的比重,并且用户自定义的 Reduce 函数满足结合律和交换律。Combiner 函数首先在本地将这些记录进行一次合并,然后将合并的结果再通过网络发送出去,Combiner 函数在每台执行 Map 任务的机器上都会被执行一次。
            一般情况下,Combiner 和 Reduce 函数是一样的。唯一的区别是 Reduce 函数的输出被保存在最终的输出文件里,Combiner 函数的输出被写到中间文件里,然后被发送给 Reduce 任务。
        4. 输入和输出的类型
            每种输入类型的实现都必须能够把输入数据分割成数据片段;可以通过提供一个简单的 Reader 接口实现,就能够支持一个新的输入类型。
        5. 副作用
            通常应用程序首先把输出结果写到一个临时文件中,在输出全部数据之后,在使用系统级的原子操作 rename 重新命名这个临时文件。
        6. 跳过损坏的记录
            为了保证保证整个处理能继续进行,MapReduce 会检测哪些记录导致系统的稳定性,并且跳过这些记录不处理。
        7. 本地执行
        8. 状态信息:master 使用嵌入式的 HTTP 服务器(如 Jetty)显示一组状态信息页面,用户可以监控各种执行状态。
        9. 计数器:计数器机制对于 MapReduce 操作的完整性检查非常有用。

五、性能

        1. 测试
        在一个大型集群上运行的两个典型的应用,衡量 MapReduce 的性能。一个计算在大约 1TB 的数据中进行特定的模式匹配(对数据格式进行转换,从一种表现形式转换为另外一种表现形式);另一个计算对大约 1TB 的数据进行排序(从海量数据中抽取少部分的用户感兴趣的数据)。  

        2. GREP

                                                                                                 结果图

        Y 轴表示输入数据的处理速度。处理速度随着参与 MapReduce 计算的机器数量的增加而增加,当 1764 台 worker 参与计算的时,处理速度达到了 30GB/s。当 Map 任务结束的时候,即在计算开始后 80 秒,输入的处理速度降到 0。

整个计算过程从开始到结束一共花了大概 150 秒。这包括了大约一分钟的初始启动阶段。初始启动阶段消耗的时间包括了是把这个程序传送到各个 worker 机器上的时间、等待 GFS 文件系统打开 1000 个输入文件集合的时间、获取相关的文件本地位置优化信息的时间。 

         3. 排序
            排序程序处理 10 的 10 次方个 100 个字节组成的记录(大概 1TB 的数据)
            

                                                                                                 结果图
            左上的图显示了输入数据读取的速度。
            排序程序输入数据读取速度小于分布式 grep 程序。这是因为排序程序的 Map 任务花了大约一半的处理时间和 I/O 带宽把中间输出结果写到本地硬盘。相应的分布式 grep 程序的中间结果输出几乎可以忽略不计。  
            左边中间的图显示了中间数据从 Map 任务发送到 Reduce 任务的网络速度。
            左下图表示 Reduce 任务把排序后的数据写到最终的输出文件的速度。
            图三(b)显示了关闭了备用任务后排序程序执行情况。
            图三(c)显示出了一个“负”的输入数据读取速度,这是因为一些已经完成的 Map 任务丢失了(由于相应的执行 Map 任务的 worker 进程被 kill 了),需要重新执行这些任务。

六、经验

        1. 应用
            大规模机器学习问题;
            从产品报告中抽取数据;
            网页中提取有用信息(如地理位置信息);
            大规模图形计算;
            网络搜索的索引系统;
        2. 大规模索引
            到目前为止,MapReduce 最成功的应用就是重写了 Google 网络搜索服务所使用到的 index 系统
            a. 实现
            索引系统的输入数据是网络爬虫抓取回来的海量的文档,这些文档数据都保存在 GFS 文件系统里。这些文档原始内容4的大小超过了 20TB。索引程序是通过一系列的 MapReduce 操作(大约 5 到 10 次)来建立索引。
            b. 优点
            实现索引部分的代码简单、小巧、容易理解,因为对于容错、分布式以及并行计算的处理都是 MapReduce 库提供的;
            概念上不相关的计算步骤的隔离也使得我们可以很容易改变索引处理方式;
            容易管理索引系统的操作。

七、相关工作

          由于 MapReduce 封装了并行处理、容错处理、数据本地化优化、负载均衡等等技术难点的细节,这使得 MapReduce 库易于使用;大量不同类型的问题都可以通过 MapReduce 简单的解决;有效利用丰富的计算资源变得非常简单,因此也适合用来解决需要大量计算的问题。

八、结束语

注:结束语作为文章的总结部分,原文如下。       

MapReduce 编程模型在 Google 内部成功应用于多个领域。我们把这种成功归结为几个方面:首先,由于MapReduce 封装了并行处理、容错处理、数据本地化优、负载均衡等等技术难点的细节,这使得 MapReduce 库易于使用。即便对于完全没有并行或者分布式系统开发经验的程序员而言;其次,大量不同类型的问题都可以通过 MapReduce 简单的解决。比如,MapReduce 用于生成 Google 的网络搜索服务所需要的数据、用来排序、用来数据挖掘、用于机器学习,以及很多其它的系统;第三,我们实现了一个在数千台计算机组成的大型集群上灵活部署运行的 MapReduce。这个实现使得有效利用这些丰富的计算资源变得非常简单,因此也适合用来解决 Google 遇到的其他很多需要大量计算的问题。  

        我们也从MapReduce开发过程中学到了不少东西。首先,约束编程模式使得并行和分布式计算非常容易,也易于构造容错的计算环境;其次,网络带宽是稀有资源。大量的系统优化是针对减少网络传输量为目的的:本地优化策略使大量的数据从本地磁盘读取,中间文件写入本地磁盘、并且只写一份中间文件也节约了网络带宽;第三,多次执行相同的任务可以减少性能缓慢的机器带来的负面影响(alex 注:即硬件配置的不平衡),同时解决了由于机器失效导致的数据丢失问题。 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值