MapReduce :大型集群上的 简单数据处理
摘要
MapReduce是一个设计模型,也是一个处理和产生海量数据的一个相关实现。用户指定一个用于处理一个键值(key-value)对生成一组key/value对形式的中间结果的map函数,以及一个将中间结果键相同的键值对合并到一起的reduce函数。许多现实世界的任务都能满足这个模型,如这篇文章所示。
使用这个功能形式实现的程序能够在大量的普通机器上并行执行。这个运行程序的系统关心下面的这些细节:输入数据的分区、一组机器上调度程序执行、处理机器失败问题,以及管理所需的机器内部的通信。这使没有任何并行处理和分布式系统经验的程序员能够利用这个大型分布式系统的资源。
我们的MapReduce实现运行在一个由普通机器组成的大规模集群上,具有很高的可扩展性:一个典型的MapReduce计算会在几千台机器上处理许多TB的数据。程序员们发现这个系统很容易使用:目前已经实现了几百个MapReduce程序,在Google的集群上,每天有超过一千个的MapReduce工作在运行。
一、 介绍
在过去的5年中,本文作者和许多Google的程序员已经实现了数百个特定用途的计算程序,处理了海量的原始数据,包括抓取到的文档、网页请求日志等,计算各种衍生出来的数据,如反向索引、网页文档的图形结构的各种表示、每个host下抓取到的页面数量的总计、一个给定日期内的最频繁查询的集合等。大多数这种计算概念明确。然而,输入数据通常都很大,并且计算必须分布到数百或数千台机器上以确保在一个合理的时间内完成。如何并行计算、分布数据、处理错误等问题使这个起初很简单的计算,由于增加了处理这些问题的很多代码而变得十分复杂。
为了解决这个复杂问题,我们设计了一个新的抽象模型,它允许我们将想要执行的计算简单的表示出来,而隐藏其中并行计算、容错、数据分布和负载均衡等很麻烦的细节。我们的抽象概念是受最早出现在lisp和其它结构性语言中的map和reduce启发的。我们认识到,大多数的计算包含对每个在输入数据中的逻辑记录执行一个map操作以获取一组中间key/value对,然后对含有相同key的所有中间值执行一个reduce操作,以此适当的合并之前的衍生数据。由用户指定map和reduce操作的功能模型允许我们能够简单的进行并行海量计算,并使用re-execution作为主要的容错机制。
这项工作的最大贡献是提供了一个简单的、强大的接口,使我们能够自动的进行并行和分布式的大规模计算,通过在由普通PC组成的大规模集群上实现高性能的接口来进行合并。
第二章描述了基本的编程模型,并给出了几个例子。第三章描述了一个为我们的聚类计算环境定制的MapReduce接口实现。第四章描述了我们发现对程序模型很有用的几个优化。第六章探索了MapReduce在Google内部的使用,包括我们在将它作为生产索引系统重写的基础的一些经验。第七章讨论了相关的和未来的工作。
二、 编程模型
这个计算输入一个key/value对集合,产生一组输出key/value对。MapReduce库的用户通过两个函数来标识这个计算:Map和Reduce。
Map,由用户编写,接收一个输入对,产生一组中间key/value对。MapReduce库将具有相同中间key I的聚合到一起,然后将它们发送给Reduce函数。
Reduce,也是由用户编写的,接收中间key I和这个key的值的集合,将这些值合并起来,形成一个尽可能小的集合。通常,每个Reduce调用只产生0或1个输出值。这些中间值经过一个迭代器(iterator)提供给用户的reduce函数。这允许我们可以处理由于数据量过大而无法载入内存的值的链表。
2.1 例子
考虑一个海量文件集中的每个单词出现次数的问题,用户会写出类似于下面的伪码:
Map函数对每个单词增加一个相应的出现次数(在这个例子中仅仅为“1”)。Reduce函数将一个指定单词所有的计数加到一起。
此外,用户使用输入和输出文件的名字、可选的调节参数编写代码,来填充一个mapreduce规格对象,然后调用MapReduce函数,并把这个对象传给它。用户的代码与MapReduce库(C++实现)连接到一起。。附录A包含了这个例子的整个程序。
2.2 类型
尽管之前的伪代码中使用了字符串格式的输入和输出,但是在概念上,用户定义的map和reduce函数需要相关联的类型:
map (k1, v1) --> list(k2, v2)
reduce (k2, list(v2)) --> list(v2)
也就是说,输入的键和值和输出的键和值来自不同的域。此外,中间结果的键和值与输出的键和值有相同的域。
MapReduce的C++实现与用户定义的函数使用字符串类型进行参数传递,将类型转换的工作留给用户的代码来处理。
2.3 更多的例子
这里有几个简单有趣的程序,能够使用MapReduce计算简单的表示出来。
分布式字符串查找( Distributed Grep ): map函数将匹配一个模式的行找出来。Reduce函数是一个恒等函数,只是将中间值拷贝到输出上。
URL 访问频率计数( Count of URL Access Frequency ): map函数处理web页面请求的日志,并输出<URL, 1>。Reduce函数将相同URL的值累加到一起,生成一个<URL, total count>对。
翻转网页连接图( Reverse Web-Link Graph ): map函数为在一个名为source的页面中指向目标(target)URL的每个链接输出<target, source>对。Reduce函数将一个给定目标URL相关的所有源(source)URLs连接成一个链表,并生成对:<target, list(source)>。
主机关键向量指标( Term-Vector per Host ): 一个检索词向量将出现在一个文档或是一组文档中最重要的单词概述为一个<word, frequency>对链表。Map函数为每个输入文档产生一个<hostname, term vector>(hostname来自文档中的URL)。Reduce函数接收一个给定hostname的所有文档检索词向量,它将这些向量累加到一起,将罕见的向量丢掉,然后生成一个最终的<hostname, term vector>对。
倒排索引( Inverted Index ): map函数解析每个文档,并生成一个<word, document ID>序列。Reduce函数接收一个给定单词的所有键值对,所有的输出对形成一个简单的倒排索引。可以通过对计算的修改来保持对单词位置的追踪。
分布式排序( Distributed Sort ): map函数将每个记录的key抽取出来,并生成一个<key,