这是一个由simviso团队所组织进行的基于mit分布式系统课程翻译的系列,由知秋带领和其余成员一块儿翻译的课程以及课程当中涉及的论文翻译。
本文章参与人员程序员
参与人员 | 参与范围 |
---|---|
知秋 | 审校 |
虚生花 | 翻译 |
概要编程
MapReduce是一种编程模型,它是一种用于处理和生成大型数据集的实现。用户经过指定一个用来处理键值对(Key/Value)的map函数来生成一个中间键值对集合。而后,再指定一个reduce函数, 它用来合并全部的具备相同中间key的中间value 。现实生活中有许多任务能够经过该模型进行表达,具体案例会在论文中展示出来。数组
以这种函数式风格编写的程序可以在一个大型商用机器集群上自动并行执行。这个系统在运行时只关心:如何分割输入数据,在大量计算机所组成的集群上的调度问题,集群中计算机的故障处理,管理集群中计算机之间的必要通讯。使用MapReduce编程模型可让那些没有并行计算和分布式系统开发经验的程序员有效的使用分布式系统的资源。网络
咱们实现的MapReduce能够在一个大型的商用计算机集群上运行,而且具有高度扩展性:一个标准的MapReduce计算能够在数千台机器上处理TB级的数据。程序员会以为该系统易于使用。目前在谷歌已经实现了数以百计的MapReduce程序,在谷歌的集群上,天天都有1000多个MapReduce的工做在执行。负载均衡
在过去五年里,做者以及许多其余在谷歌工做的人已经实现了数百种用于特殊目的的计算。它们能够用来处理大量原始数据,例如:爬取的文档,网页请求日志等等。并以此来计算出各类衍生数据,例如:倒排索引,Web文档的各类图表示,每台主机所抓取页面数的摘要,以及特定某天中最频繁的查询集等等。大部分这种计算从概念上来说都很简单。可是,输入的数据量一般很是巨大,而且为了能在一个合理的时间内完成,计算任务也不得不分配给成百上千台机器去执行。如何并行化计算,分配数据以及处理故障的问题,全部的问题都纠缠在一块儿,这就须要大量的代码来对它们进行处理。所以,这也使得本来简单的计算变得极为复杂,并且难以处理。分布式
为了应对这种复杂性,咱们设计了一种新的抽象,它可让咱们表达咱们所试图执行的简单计算,但该库中隐藏了并行化,容错,数据分发以及负载均衡这些混乱的细节。咱们这种抽象的设计灵感来源于Lisp和许多其余函数式语言中存在的map和reduce原语。咱们意识到,大多数计算都涉及到对输入中的每一个逻辑记录进行_map_操做,以便于计算出一个中间键值对的集合。而后,为了恰当的整合衍生数据,咱们对共用相同键的全部值进行_reduce_操做。经过使用具有用户所指定的_map_和_reduce_操做的函数式模型,这使得咱们可以轻松并行化大型计算,而且使用从新执行的结果做为容错的主要机制。函数
这项工做的主要贡献在于提供了一个简单而强大的接口,该接口可实现大规模计算的自动并行化和分布式执行。经过使用该接口的实现,从而在大型商用计算机集群上得到了高性能。性能
本文的第2章节描述了该基础编程模型并给出了一些案例。第3章节则是关于咱们针对集群的计算环境所量身定制的MapReduce接口的实现。第4章节介绍了咱们所找到的对于该编程模型的一些有用改进。第5章节则是关于咱们经过一系列任务对咱们所实现的MapReduce进行的性能测试。第6章节则探索了MapReduce在谷歌中的一些应用,这其中包括了咱们使用它来重写咱们的索引系统的一些经验。第7章节讨论了一些相关以及往后要作的工做。测试
该计算任务将一个键值对集合做为输入,并生成一个键值对集合做为输出。MapReduce这个库的用户将这种计算任务以两个函数进行表达,即Map和Reduce。spa
由用户所编写的Map函数接收输入,并生成一个中间键值对集合。MapReduce这个库会将全部共用一个键的值组合在一块儿,并将它们传递给Reduce函数。
Reduce函数也是由用户所编写。它接受一个中间键以及该键的值的集合做为输入。它会将这些值合并在一块儿,以此来生成一组更小的值的集合。一般每次调用Reduce函数所产生的值的结果只有0个或者1个。中间值经过一个迭代器来传递给用户所编写的Reduce函数。这使得咱们能够处理这些由于数据量太大而没法存放在内存中的存储值的list列表了。
咱们能够思考下这样一个场景,咱们要从大量的文档中计算出每一个单词的出现次数。用户将会编写出相似于下方伪代码的代码:
map(Stringkey,Stringvalue): // 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中会提供该案例的完整代码。
尽管在前面的伪代码中的输入和输出的类型都是String,可是从概念上来讲,用户所提供的map
和reduce
函数都有相关联的类型。
map(k1,v1) -->list(k2,v2) reduce(k2,list(v2)) -->list(v2)
例如,输入的键和值与输出的键和值来自于不一样的地方。此外,中间的键和值与输出的键和值在类型上相同。
在咱们的C++实现中,咱们使用String类型做为用户所定义的函数的输入和输出的类型,用户在本身的代码中对字符串进行适当的类型转换。
此处有一些能够很容易使用MapReduce模型来表示的简单例子:
分布式过滤器:Map
函数会发出(emit)匹配某个规则的一行。Reduce
函数是一个恒等函数,即把中间数据复制到输出。(虚生花注:恒等函数是数学中是一种没有任何做用的函数,它的输入等于输出,即f(x)=x)。
计算URL的访问频率:map
函数用来处理网页请求的日志,并输出(URL,1)。reduce
函数则用于将相同URL的值所有加起来,并输出(URL, 访问总次数)
这样的键值对结果。
倒转网络连接图:map
函数会在源页面中找到全部的目标URL,并输出<target, source>这样的键值对。reduce
函数会将给定的目标URL的全部连接组合成一个列表,输出<target, list(source)>这样的键值对。
每台主机上的检索词频率:term(这里是指搜索系统里的某一项东西,这里指检索词)vector(这里指数组)将一个文档或者是一组文档中出现的最重要的单词归纳为_<单词,频率>_ 这样的键值对列表,对于每一个输入文档,map函数会输出这样一对键值对<hostname, term vector>(其中hostname是从文档中的URL里提取出来的)。Reduce函数接收给定主机的全部每个文档的term vector。它会将这些term vector加在一块儿,并去除频率较低的term,而后输出一个最终键值对<hostname, term vector>。
倒排索引:map
函数会对每一个文档进行解析,并输出<word, 文档ID>这样的键值对序列。reduce
函数所接受的输入是一个给定词的全部键值对,接着它会对全部文档ID进行排序,而后输出<word, list(文档ID)>。全部输出键值对的集合能够造成一个简单的倒排索引。咱们能简单的计算出每一个单词在文档中的位置。
分布式排序:map
函数会从每条记录中提取出一个key,而后输出<key, record>这样的键值对。reduce
函数对这些键值对不作任何修改,直接输出。这种计算任务依赖分区机制(详见章节4.1)以及排序属性(详见章节4.2)。