MapReduce:大型集群上简便的数据处理

摘要

MapReduce是一种编程模型,也是一种处理和生成大型数据集的相应实现。用户指定处理键/值对以生成一组中间键/值对的map函数,以及合并与相同中间键关联的所有中间值的reduce函数。如本文所示,该模型中可以表达许多现实的任务。

以此函数风格编写的程序将自动并行化并在大型商用机器集群上运行。运行时系统负责对输入数据分区的细节,调度程序在一组机器上的执行,处理机器故障以及管理所需的机器间通信。这使得没有任何并行和分布式系统经验的开发者可以轻松利用大型分布式系统的资源。

我们的MapReduce实现在大型商用机器集群上运行,并且具有高度可扩展性:典型的MapReduce计算可处理数千台机器上的数TB数据。开发者发现该系统易于使用:已经实施了数百个MapReduce程序,每天在Google的集群上执行超过一千个MapReduce作业。

1. 简介

在过去五年中,在谷歌工作的作者和许多其他人已经实施了数百个专业计算,这些计算处理大量原始数据,例如爬虫文档,Web请求日志等,以计算各种派生数据,例如作为反向索引,Web文档的图形结构的各种表示,每台机器爬取的页面数量的摘要,给定日期中最频繁查询的集合等。大多数这样的计算在概念上是直截了当的。但是,输入数据通常很大,并且计算必须分布在数百或数千台机器上,以便在合理的时间内完成。如何并行化计算、分配数据和处理故障的问题使得需要大量复杂代码处理这些问题,从而隐藏了原始的简单计算。

为应对这种复杂性,我们设计了一种新的抽象,它允许我们表达我们试图执行的简单计算,并且在库中隐藏了并行化、容错、数据分布和负载均衡的烦琐细节。这种抽象的灵感来自Lisp和许多其他函数式语言中的map和reduce原语。我们意识到我们的大多数计算都涉及将map操作应用于输入中的每个逻辑“记录”,以便计算出一组中间键/值对,然后对拥有相同键的所有值应用reduce操作,以适当地生成派生数据。我们使用具有用户指定的map和reduce操作的函数模型,使我们能够轻松地并行化大型计算并使用重新执行作为容错的主要机制。

这项工作的主要贡献是一个简单而强大的可实现大规模计算的自动并行化和分发的接口,并结合在大型商用PC集群上对该接口高性能的实现。

第2节描述了基本的编程模型,并给出了几个示例。第3节描述了针对基于集群的计算环境定制的MapReduce接口的实现。第4节描述了我们发现的针对编程模型有用的几个改进。第5节对各种任务的实现进行了性能测试。第6节探讨了MapReduce在Google中的使用,包括我们使用它作为重写线上索引系统基础的经验。第7部分讨论了相关的和未来的工作。

2. 编程模型

计算拿到一组输入键/值对,并产生一组输出键/值对。MapReduce库的用户将计算表达为两个函数:Map和Reduce。

由用户编写的Map获取到一个输入对并产生一组中间键/值对。MapReduce库将具有相同中间键I的所有中间值聚合在一起,并将它们传递给Reduce函数。

Reduce函数也是由用户编写的,它接受一个中间键I以及该键值对应的一组中间值。它将这些值合并在一起,形成一组可能较小的值。通常,每次Reduce调用只产生零个或一个输出值。中间值通过迭代器提供给用户的reduce函数。这允许我们可以处理太大而不适合放入内存中的值列表。

2.1 示例

考虑统计大量文档中每个单词出现次数的问题。用户将编写类似于以下伪代码的代码:

map(String key, String value):
  // key: document name
  // value: document contents
  for each word w in value:
    EmitIntermediate(w, "1");

reduce(String key, Iterator values):
  // key: a word
  // values: a list of counts
  int result = 0;
  for each v in values:
    result += ParseInt(v);
  Emit(AsString(result));

map函数产出每个单词加上相关的出现次数(在这个简单的例子中只是’1’)。reduce函数将特定单词生成的所有次数加总在一起。

此外,用户编写代码以输入和输出文件的名称以及可选的调整参数填充mapreduce规范对象。然后,用户调用MapReduce函数,并将规范对象传递给它。用户的代码与MapReduce库(用C ++实现)链接在一起。附录A包含此示例的完整程序文本。

2.2 类型

尽管先前的伪代码是根据字符串输入和输出编写的,但从概念上讲,用户提供的map和reduce函数具有相关的类型:

map(k1,v1) -> list(k2,v2)
reduce(k2,list(v2)) -> list(v2)

即,输入键和值是来自与输出键和值不同的域中。此外,中间键和值来自与输出键和值相同的域。

我们的C++实现将字符串传递给用户定义的函数以及从中获取,并将自定义函数留给用户代码实现以在字符串和适当的类型之间进行转换。

2.3 更多示例

以下是一些有趣程序的简单示例,可以很容易地表示为MapReduce计算。

  • 分布式Grep:如果map函数与提供的模式匹配,则会产出一行。reduce函数是一个恒等函数,它只是将提供的中间数据复制到输出。
  • URL访问频率计数:map函数处理网页请求日志并输出<URL,1>。reduce函数将同一URL的所有值相加,并输出<URL,total count>对。
  • 反向Web链接图:map函数将每个链接输出<target,source>对,表示在名为source的页面中找到了target链接。reduce函数将与给定target链接关联的所有source链接的列表拼接,并输出一对:<target,list(source)>。
  • 每个主机的关键词向量:关键词向量将文档或一组文档中出现的最重要的单词概括为<word,frequency>对的列表。map函数为每个输入文档输出<hostname,term vector>对(其中主机名是从文档的URL中提取的)。将给定主机的所有文档关键词向量传递给reduce函数。它将这些关键词向量加到一起,丢弃不常用的关键词,然后得到一个最终的<hostname,term vector>对。
  • 反向索引:map函数解析每个文档,并产出一系列<word,document ID>对。reduce函数接受给定单词的所有对,对相应的文档ID进行排序并输出<word,list(document ID)>对。所有输出对的集合形成简单的倒排索引。很容易扩大该计算以跟踪单词位置。
  • 分布式排序:map函数从每个记录中提取key,并产出<key,record>对。reduce函数未加改变地输出所有对。此计算依赖于第4.1节中描述的分区工具和第4.2节中描述的排序属性。

3. 实现

MapReduce接口可能有许多不同的实现。正确的选择取决于环境。例如,一种实现方式可能适用于小型共享内存机器,另一种实现方式适用于大型NUMA多核处理器,而另一种实现方式适用于甚至更大型的联网机器集合。

本节描述的一种实现是针对Google广泛使用的计算环境:使用交换式以太网连接在一起的大型商用PC集群[4]。在我们的环境中:

  1. 机器通常是运行Linux的双核x86处理器,每台机器有2-4GB的内存。
  2. 使用商用网络硬件 - 通常在机器级别上为100Mb/s或1Gb/s,但在总体双向带宽下平均下来要少得多。
  3. 集群由数百或数千台机器组成,因此机器故障很常见。
  4. 存储由直接连接到各个机器的廉价IDE磁盘提供。使用内部开发的分布式文件系统[8]来管理存储在这些磁盘上的数据。文件系统使用复本在不可靠的硬件之上提供可用性和可靠性。
  5. 用户将作业提交给调度系统。每个作业由一组任务组成,并由调度器划分到集群中的一组可用机器上。

3.1 执行概述

通过自动将输入数据分区为一组M个分片,在多台机器上分布式调用Map。输入分片可以由不同的机器并行处理。通过使用分区函数(例如,hash(key) mod R),将中间键key空间划分为R片来分发给reduce调用。分区数(R)和分区函数由用户指定。

图1:执行概述

图1显示了我们实现的MapReduce操作的总体流程。当用户程序调用MapReduce函数时,会发生以下操作序列(图1中的编号标签对应于下面列表中的数字):

  1. 用户程序中的MapReduce库首先将输入文件划分为每片通常16MB到64MB的M个分片(可由用户通过可选参数控制)。然后它在一组机器上启动程序的许多副本。
  2. 该程序的其中一个副本是特殊的 - master。其余的是由master分配工作的worker。有M个map任务和R个reduce任务要分配。Master挑选闲置的worker并为每一个分配一个map任务或reduce任务。
  3. 分配了map任务的worker将读取相应输入分片的内容。它从输入数据中解析键/值对,并将每个键/值对传递给用户定义的Map函数。Map函数生成的中间键/值对缓存在内存中。
  4. 缓存的中间键值对会被周期性地写入本地磁盘,通过分区函数划分为R个区域。这些缓存对在本地磁盘上的位置将回传给master,它负责将这些位置转发给reduce任务的worker。
  5. 当执行reduce任务的worker收到master关于这些位置的通知后,它使用远程过程调用从执行map任务的worker的本地磁盘中读取缓存数据。当reduce任务的worker读取了所有中间数据时,它会通过中间结果的键对其进行排序,从而将所有出现的相同键的键值对组合在一起。排序是必要的,因为通常许多不同的键映射到同一个reduce任务。如果中间结果数据量太大而无法容纳在内存中,则使用外部排序。
  6. reduce任务的worker遍历已排序的中间数据,并且对于遇到的每个唯一中间键,它将该键和相应的中间值集合传递给用户的Reduce函数。Reduce函数的输出附加到此reduce分区的最终输出文件中。
  7. 完成所有map任务和reduce任务后,master会唤醒用户程序。此时,用户程序中的MapReduce调用将返回到用户代码。

成功完成后,MapReduce执行的输出将在R个输出文件中可用(每个reduce任务一个&

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值