原文链接:http://stanford.edu/class/cs345d-01/rl/mapreduce.pdf
MapReduce:在大规模集群上简化数据处理
作者:Jeffrey Dean和Sanjay Ghemawat(jeff@google.com, sanjay@google.com)
摘要
mapReduce是一个处理和生成海量数据集合的编程模型以及相关实现。用户需要声明一个map函数以及一个reduce函数,map函数处理(key, value)对,生成中间结果集,中间结果集也是(key, value)对;reduce函数合并中间结果集中相同的key对应的值。正如本文所述,此模型可以表示很多现实问题。
按此模型编写的程序可以在大型商业集群上自动并行执行。运行系统负责数据分区,程序执行调度,机器故障处理以及内部物理机通信管理等细节。使得程序员可以在没有任何并行系统以及分布式系统的经验下,很容易地充分利用大型分布式系统的资源。
我们的MapReduce程序在大型商业集群上运行,并且易于扩展:一个典型的MapReduce计算程序可以在几千台机器上处理TB级别的数据。程序员觉得系统易于使用:已经实现了几百个MapReduce程序,每天有超过一千个MapReduce任务在谷歌集群上运行。
1. 介绍
在过去五年,作者和其他的谷歌同事实现了几百个不同目的的计算程序,用以处理大量的数据:比如爬虫文档,浏览器请求日志等,计算会产生一系列输出数据,例如:倒排索引,不同的图结构用于表示浏览器文档,单个主机爬虫页面数的总和,一天内最频繁的查询集等。大多数的计算简单直接。然而输入数据体量庞大,为了在合理的时间内完成,这些计算必须在几百上千个机器上同时进行。为了解决如何并行运行这些计算,分布数据以及处理故障,原本简单的计算需要大量复杂的代码。
为了解决这种复杂性,我们设计了一种新的抽象,用以表示想要执行的简单的计算,将并行化,容错,数据分布,以及负载均衡等这些繁琐的细节抽象到一个库中。该抽象受Lisp和一些其他语言中的map和recude原语的启发。我们意识到大多数的计算包含两步:
- 对于输入中的每个逻辑上的“记录”,应用map操作,计算得到中间的(key,value)结果集;
- 中间结果集中,对于相同的key,在所有values上应用reduce操作,适当地组合生成的数据。
用户声明map和reduce操作的函数模型让我们可以很容易地并行化海量计算,并且使用重试作为主要的容错机制。
本文的主要贡献在于提出了一个简单强大的接口,可支持自动化并行处理,海量计算分布; 并提供了接口的实现,在商业计算集群上具有较优的性能。
第二节描述了基本的编程模型,给出了几个例子。第三节针对基于集群的计算环境,描述了mapReduce接口的实现。第四节描述了我们此编程模型的几个有用的优化点。第五节展示了各种任务上,该实现的性能测试。第六节展示了MapReduce在谷歌内部的使用经验,包括在生产索引系统重写中,使用该模型作为基础。第七节讨论了未来相关工作。
2. 编程模型
计算接受(key,value)集合作为输入,输出(key,value)集合。MapReduce工具库的用户可以将计算表示为两个函数:Map和Reduce。
Map函数:由用户定义;接受输入对,产生中间结果((key,value)对集合)。mapReduce库将具有相同key的中间结果分为1组,传递给reduce函数。
Reduce函数:也由用户定义;接受一个中间结果key以及key值对应的value集合作为输入,会合并该value集合为更小的value集合。通常每次reduce调用产生0个或者1个输出值。reduce函数通过一个迭代器遍历中间值,以此处理无法全部加载在内存中的海量的value值。
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’),recude函数将特定单词的所有词频相加。
除此之外,用户需要在一个MapReduce规范对象中指定输入输出文件以及可选的调优参数。接着调用MapReduce函数,传递给该规范对象。用户代码和MapReduce库(c++实现)连在一起。附录A包含本例的完整程序。
2.2 类型
虽然之前伪代码的输入输出为string类型,理论上,用户提供的map和reduce函数具有相关的类型:
map (k1,v1) → list(k2,v2)
reduce (k2,list(v2)) → list(v2)
也就是说,输入的keys和values与输出的keys和values值域不同。此外,中间结果中的keys和values和输出的keys和values值域相同。
我们的c++实现和用户定义的函数之间传递string类型的值。并让用户程序处理string类型和其它类型的转换。
2.3 更多的例子
还有一些有趣的项目,可以很容易地被表示为MapReduce计算:
分布式查找:如果匹配提供的格式,map函数会输出该行;reduce 函数是一个恒等函数,它只是将提供的中间数据拷贝到输出文件。
URL访问频率:map函数处理网页请求日志,并输出<URL, 1>;Reduce函数将同一URL的所有值相加,生成<URL, 总数>对。
Link-Web图:对于源页面中每一个到目标URL的链接,map函数会输出<目标URL,源页面>对;reduce函数连接特定目标URL的所有源页面,生成<目标URL,List(源页面)>对。
单机term向量:term向量总结了出现在单个文档/文档集中的最重要的单词,也就是<单词