说明
本文是阅读MapReduce
论文后写的笔记,大部分内容来自于原文的直接翻译,少部分部分补充了自己觉得阅读者可能有疑问的问题,同时对于这些问题自己给出了一些见解,这些见解可能并不一定正确,欢迎大家斧正。
如果你觉得阅读英文的文章很困难,那么阅读本篇笔记来代替阅读原文是你不错的选择。
论文原文链接
MapReduce: Simplified Data Processing on Large Clusters
ABS
MapReduce
是一个用于处理和生成大量数据集合的编程模型,开发者可以利用MapReduce
进行分布式开发。开发者只需要编写map函数和reduce
函数,map
函数用来产生中间的key-value
对,而reduce
函数负责处理key
相同的中间key-value
对。
MapReduce
很符合函数式编程的定义,用户只需要编写两个函数既可以完成相关工作,而函数会在分布式系统上并行执行,这些都是用户不需要关心的。数据如何在分布式系统中分发,分布式系统的冗余处理都是由系统自动完成,即使没有分布式开发经验的开发人员也可以利用该系统开发出功能强大的分布式应用。
注:MapReduce
的受众群体是需要是需要利用分布式来开发应用的开发人员,所以用户是指开发人员。
1 INTRO
设计这样一个系统的起因是,Google
内部的很多计算需要用到分布式计算,例如处理爬虫爬取的文件、统计页面访问次数等,而在分布式系统中进行并行需要考虑很多复杂问题。而处理分布式中的这些问题会让一个原本简单的问题变得非常复杂(例如原始问题是统计单词的统计次数,但是如果在分布式系统中进行,那么需要考虑的问题包括但不限于:某台主机宕机,如何分发数据)。
而设计MapReduce
就只需要考虑原始的问题,中间数据分发的细节,宕机的处理等等这些全部由这个系统自己处理。
作者提到MapReduce
这个名字来源于Lisp
中的原语map
和reduce
,同时公司的人员发现他们很多需要借助分布式的工作可以被分解成map
和reduce
的过程。
本文的贡献:提出了一个简单但是功能强大的接口,该接口具有以下功能:
- 自动并行计算;
- 大规模计算的分布式处理;
除了提供接口之外,还提供了接口的高性能实现。
2 Programming Model
MapReduce
的输入是一系列的键值对(例如文件名-文件内容),输出也是一系列的键值对(例如单词-出现次数)。用户只需要编写map
函数和reduce
函数。
map
函数:该函数会输出中间键值对,对于所有的输出MapReduce
的底层会自动将不同结点上的相同的key
的数据(这里实际上还有一个partition
的过程后文会详细介绍)发送到同一个reduce
函数进行处理。
reduce
函数:处理接收到的键值对数据,不同结点上的map
函数产生具有相同key
的value
会被拼接成一个列表传递给reduce
函数。
2.1 Example
经典例子:统计许多文件组成的文件集合中单词的出现次数。
解决该问题的伪代码如下:
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
1
1。而reduce
则是将传递过来的中间值进行累加即可的都整个文件集合的单词出现次数。
2.2 Types
可以上面的代码使用的是string
作为数据类型,但是在MapReduce
的实现中存在着类型转换,其中map
函数的输入类型与其输出类型是不同的(来自不同的域,例如上面的例子中,输入是文件名和文件内容而输出单词和对应的出现次数),而中间值的类型与reduce
函数的输出类型是相同的(来自同一域,这里可以理解成有相同的取值范围)。
下面的代码可以很好的解释这一点:
map (k1,v1) → list(k2,v2)
reduce (k2,list(v2)) → list(v2)
不同用户不用担心这一点,这些不同结果由MapReduce
自动完成相应的类型转换以保证结果的正确性。
2.3 More Examples
说明
1
1
1:grep
是unix/linux
下的一个工具用于过滤出输入中满足模式串的内容。
说明
2
2
2:Reverse Web-Link Graph
是指统计某个target
网址能够由哪些source
跳转。
说明
3
3
3:Term-Vector per Host
是指统计每个Host
的关键字,如果数据是Host
的访问记录,那么关键字可以理解为Host
最常访问的某几个网站的网址。
说明
4
4
4:Inverted Index
表示倒排索引,这里的例子是需要统计每个词语出现在哪些文件中。
Distributed Grep
:在这个例子中map
函数会产生出满足模式串的内容,而reduce
函数只需要将这些内容原封不动的进行输出即可。Count of URL Access Frequency
:该例子与统计单词个数是类似的,只是单词变成了URL
。Reverse Web-Link Graph
:map
函数只需要输出<target, source>
键值对,与单词统计不同的是,这里是将source
放到列表中(或者以分割符分割的形式进行拼接);reduce
则只需要将收到的value
中的值全部拼接起来,就可以产生<target, source>
的输出表示target
可以由source
中的网址跳转而来。Term-Vector per Host
:map
只需要统计出自己输入的关键字产生<host, term vector>
中间值;reduce
将这些值全部拼接起来,然后再按照关键程度排序保留最关键的几个值即可。- 例如需要统计用户最长访问的
5
5
5个网站,如果可以的话,各个结点的
map
都需要找到最高频率的五个网站,同时需要将网站的访问频率一同发送,如果有五个结点运行map
并且每个节点都找到了各自文件中每个最常访问的 5 5 5个(可能少于 5 5 5个)网站,那么对于reduce
处理每个用户的时候,将所有value
进行拼接,可能得到多余 5 5 5个网站,这个时候再从这些网站中选取频率最高的 5 5 5个网站作为最终结果即可。
- 例如需要统计用户最长访问的
5
5
5个网站,如果可以的话,各个结点的
Inverted Index
:map
函数只需要统计出<word, document ID>
,而reduce
只需要将获得的<word, list(document ID)>
的每个单词的list
按照文件的ID
进行排序即可。Distributed Sort
:这里的内容会在第四部分详细讲解(涉及partition
的过程)。
3 Implementation
不同的情况下需要使用不同的MapReduce
实现,这里作者根据Google
内部商用PC
集群提出了一种实现方法。
环境介绍:
- 每个结点有两个
x86
处理器,2-4GB
的内存; - 机器级别的网络速度为
100Mbps
或者1Gbps
(这里应该是指网卡支持的最大速度),但实际的网络速度远比这个小得多; - 十万级别的结点个数;
- 存在一个内部开发的分布式文件系统来管理存放在磁盘上的数据。该文件系统通过将数据进行复制来保证可靠性和可用性。
- 用户提交
jobs
给调度系统,调度系统会将每个job
细分为多个tasks
,每个job
由调度系统分发给一系列结点进行执行。
3.1 Execution Overview
系统会自动将所有的输入分成M
份,而对于这M
份输入的处理,可以分给多个结点进行处理。对于产生的中间值,系统会使用用户提供的partition
函数将其分成R
份(这里partition
函数和R
均由用户提供,如果不提供默认的是根据key
的一个哈希,即hash(key)%R
相同的会被分到同一部分)。
Figure 1
展现了整个MapReduce
的过程
Figure 1
中的各个步骤描述如下:
- 首先会根据用户指定的分片大小将所有输入文件分成若干个分片。用户的程序会在许多的结点上启动。
- 众多运行程序的节点中存在一个主节点(图中的
Master
),主节点将会决定哪些节点执行map
函数,那些结点执行reduce
工作,实际上当一个结点空闲时间,主节点就会选择安排map
或者reduce
工作给这个空闲的节点。 - 执行
map
的工作节点会将分配给自己处理的数据在自己本地执行产生的中间键值对会暂时保存在自己的内存中。 - 存储在内存中的中间结果会周期性的写入到磁盘中,这些键值对再存入之前会执行用户提供的
partition
将中间值分成R
个部分,每个部分的存储位置会发送给主节点,主节点会将这些信息发送给执行reduce
的结点。 - 当一个执行
reduce
的工作结点能够开始工作时,该结点会通过RPC
获取到中间键值对,然后按照key
进行排序,这一步是为了让相同的key
能够连续出现(虽然相同的key
会被分到同一个结点进行处理,但是这些数据中可能并不只有一个key
所以需要进行一次排序)。当中间结果过多而不能一次性读入内存时,这时会需要进行额外的排序。 - 执行
reduce
的结点会遍历排序后的中间键值对,然后将同一个key
的value
拼接起来作为参数传递给reduce
函数,每个结点将自己的最终的结果输出到各自的一个文件中。 - 当所有
map
和reduce
执行完成后,用户程序对应的进程会被唤醒,此时会继续执行用户进程的后续部分。
最终会输出R
个文件(每一个reduce task
会输出一个文件),用户可以将这些文件继续作为另一个MapReduce
的输入以执行其他的分布式计算。
3.2 Master Data Structures
主节点会记录每个task
的状态,包含:空闲,进行中,完成。同时还会为非空闲的tasks
记录每个task
各自由那个结点执行。
同时主节点还需要记录由map
产生的R
个文件的位置和大小。这些信息会按照增量信息的方法传递给执行reduce
任务的结点。
3.3 Fault Tolerance
工作结点故障
主节点会周期性的对工作结点进行ping
操作。如果对某个工作结点进行ping
操作之后的一定时间内没有收到回复(可以理解为一段时间内ping
不通),那么主节点会将该工作结点标记为故障状态。为了进行后续的工作,所有由该结点完成的map tasks
将会全部被设置为idle
状态(即未开始状态),这样设置是为了这些任务能够重新被分配到正常的工作结点上进行执行。当然正在由该节点执行的任务不论是map task
还是reduce task
都会被设置为idle
状态。
为什么完成了的map tasks
需要重新执行,而完成了的reduce tasks
不需要重新被执行?
这是因为
map tasks
的输出被保存在工作结点的本地磁盘上,如果一个工作结点宕机了,那么其他工作结点是无法获取这一部分的数据;而reduce tasks
的输出保存在一个全局的文件系统上,某个工作结点宕机,之前的reduce tasks
的输出依然可以被访问。
那为什么map tasks
的输出不保存到一个全局的文件系统上呢?
这里文章中没有给出这一部分的说明,不过我猜测是因为,
map tasks
的输出主要是作为reduce tasks
的输入使用,当reduce tasks
完成后这一部分数据就没有太大的价值,所以没有将这一部分数据放到一个共享的文件系统上。
当一个工作结点故障后,执行reduce
的结点会从新的结点去获取中间键值对。
这样的设计特性即使有大量的工作结点故障,最终也能够完成分布式任务。
主节点故障
在只有一个主节点的情况下,如果主节点故障(这通常不会发生),那么整个任务会终止,用户可以重新执行该任务。
而如果有多个可以承担主节点工作的结点,当主节点故障了,只需要让另一个可以承担主节点工作的结点担任主节点即可(这些主节点需要保存和主节点的数据结构,例如那些任务还没被完成,而这些数据可能并不会和主节点完全一致,可能会落后几轮,但这并不会影响结果的正确性)
故障时的语义
当用户的函数(map
和reduce
)对于相同的输入在多次执行能够保障同样的输出,那么MapReduce
系统能够保证出现故障的情况下的输出与不出现故障的情况下一样。
保证map tasks
和reduce tasks
的输出的提交过程是原子性的,是上述特性的实现方式。当一个map task
执行完成之后,会向主结点发送中间输出文件的名字,主结点需要记录这些文件及其位置等信息,如果主节点又收到了一个已经完成的map task
的完成信息的时候,主节点会忽略这些信息。
为什么主节点会收到重复的完成信息?
一是主节点发送了
ping
之后没有收到响应信息,但是实际上工作结点没有宕机,那么后续可能会受到新结点发送的某个map task
的完成信息和这个“宕机”结点的完成信息。
二是可能出现straggler的情况,3.6 Backup Tasks
部分会详细介绍。
如果有多个节点执行同一个reduce task
那么对于输出文件的原子操作会保证最后只有一个输出文件。
为什么会有多个结点执行同一个reduce task
?
一是可能当主节点
ping
之后没有收到响应信息,但是这个时候工作结点并没有宕机,此时主节点将该工作结点的reduce task
给其他工作结点执行,这样就会产生两从输出文件;
二是可能出现straggler的情况,3.6 Backup Tasks
部分会详细介绍。
对于非确定性的程序(指对于相同的输出多次运行后的输出可能会不同),某个特定的reduce task
会得到与串行运行相同的输出,而其余的reduce task
输出则不一定能够保证。
3.4 Locality
输入文件会被拆分成若干份大小为64 MB
的块,对于每一个块通常会有三个工作结点会对其进行保存(也就是一个块同时存在三个结点上)。这样的话,当给某个快分配map task
进行处理的时候,会优先让这个任务在拥有这个块的结点上处理(也就是三个结点中的某一个结点),这样就不不需要进行文件的传输。而如果没有这样的节点(意味着这三个节点可能正在执行其他的任务),那么会优先将任务分配给距离含有数据的结点更近的结点(这里通常是指处于同一个子网下的两个结点)这样在进行数据传输的时候可以不需要太多的网络带宽。
上述的描述可以看出,同一个块的三份数据应该保存在不同的子网中这样才能是效率尽可能的提升。
向不同的结点上面复制三份数据不也会消耗大量的带宽吗,既然是这样为什么这样做反而能提升性能?
一开始的分发过程是发生在系统开始计算之前,这样的过程是统筹安排的,实际上对于一个确定的带宽发送时间应该是确定的。但是如果一开始并不将数据发送给各个机器,而是在执行过程中才一次次的获取数据,那么可以出现更严重的情况是,某些节点明明完成了任务,但是因为迟迟不能获取数据而导致这些计算资源没有能够被利用起来,从而很容易导致整体的速度下降(这在结点个数增多的情况下会更加明显)。
3.4 Task Granularity
根据前面的讨论可以发现会有M
个map tasks
和R
个reduce tasks
。
往往会将map job
分成远多于结点个数的map tasks
这样有助于动态的负载均衡。
同时我们可以根据复杂度来决定M
和R
的大小,可以知道主节点需要做
O
(
M
+
R
)
O(M+R)
O(M+R)次的调度,而存储状态的空间复杂度为
O
(
M
R
)
O(MR)
O(MR)(由前面可以知道主节点需要存储每个map task
产生的R
个文件的信息,所以是两者相乘)。
同时通常会选择
R
R
R的大小是工作结点的几倍,例如文中提到了的一组选择是:
M
=
200
,
000
R
=
5
,
000
M=200,000\ R=5,000
M=200,000 R=5,000工作结点的个数为
2000
2000
2000。
3.6 Backup Tasks
straggler
:掉队者,指的是在大多数任务完成后,最后几个还在执行任务的结点,通常这些结点的执行速度会变得非常慢。而执行速度变慢的原因有很多,可能磁盘IO
速度下降,CPU
被其他任务占用,内存不够导致频繁的发生交换操作等。
为了缓解straggler
出现的情况,主节点在发现最后还有几个任务执行很慢的情况下,会把这几个任务分配给其他的空闲结点,而主节点会保留最先收到的完成信息,这也解释了为什么可能会有多个结点执行同一个task
的情况(3.3中提出的问题)。
4 Refinements
4.1 Partitioning Function
默认情况下的partitioning function
是hash(key) mod R
但是有时候我们需要根据实际情况来自定义partitioning function
例如对于key
是URL
的情况,我们可能需要将同一个服务器的URL
相关数据输出到同一个输出文件,那么此时我们可以选择hash(hostname(URL)) mod R
作为partitioning function
。
4.2 Ordering Guarantees
保证处理中间键值对的时候是按照key
的升序顺序这里,这样能够保证输出文件是有序的。这样做可能会方便用户后续的处理,例如查找某个数据(在有序而数据中进行查找的耗时要比无序的数据少)。
4.3 Combiner Function
combiner function
可以理解就是在执行map tasks
完成后,在该结点上执行一次reduce
函数以达到减少网络带宽使用。以word counting
为例,由于二八原理的存在,一个map task
的中间输出可能会有成百上千条<the, 1>
(因为the
很常用),而如果不对这些数据进行combiner function
那么需要传送成百上千条<the, 1>
而如果做了一次combiner function
也就是本地做一次reduce
如果the
在本次任务的输入中出现了
100
100
100次,那么传递数据就会变成<the, 100>
传递的数据量大大减少,从而达到节约网络带宽的目的。
4.4 Input and Output Types
除了使用提供的text
类型作为输入输出类型之外,用户可以通过实现reader
来自定义输入类型,当然输出文件的类型也可以自定义。
4.5 Side-effects
用户可能需要输出其他的文件来方便后续的操作,需要注意的是MapReduce
并不能保证这些文件输出的原子性,所以这一部分文件的原子性和幂等性(指多次执行输出相同)应该由用户来保证。
4.6 Skipping Bad records
MapReduce
的一种容错机制,在处理某些tasks
的时候,这些tasks
读取的数据可能存在问题而导致程序崩溃。为了能够让整个程序继续执行,当一次执行出现崩溃(一般是指收到段错误或者总线错误)此时执行任务的结点会发送任务的索引并告知主节点该任务执行失败,如果主节点收到某个任务超过一次(这通常意味着该任务在不同的主机上均出现了错误),那么主节点会跳过该任务,不需要处理这一部分的数据。
这么做的合理性?
需要进行分布式计算的数据量往往非常巨大,而一个
task
的数据粒度相比所有数据是非常小的,在这种情况下丢弃一些tasks
不会导致最终结果的准确性(这是大数据的特性之一)。
4.7 Local Execution
这一部分是为了用户调试所设计的。如果直接在实际环境上进行调试,由于任务的有哪一个结点执行是动态的,进行调试难度较大。而MapReduce
提供了本地调试的库,用户可以指定各个任务由哪些结点执行。
4.8 Status Information
提供了一个性能检测的页面,可以看到各种各样的详细信息。
4.9 Counters
提供了计数器,计数器信息是在heartbeat
过程中的响应信息携带的,会周期性的发送给主节点,主节点会将计数器的信息显示在性能监控页面上,同时主节点会自动去除任务重复执行而导致计数器增长的数据。
例如要统计大写单词的数量,则可以使用下面的伪代码:
Counter* uppercase;
uppercase = GetCounter("uppercase");
map(String name, String contents):
for each word w in contents:
if (IsCapitalized(w)):
uppercase->Increment();
EmitIntermediate(w, "1");
5 Performance
5.1 Cluster Configuration
大约
1800
1800
1800个结点。每个节点及其有两块启用超线程的
2
G
H
z
2GHz
2GHz的CPU
,
4
G
B
4GB
4GB的内存,两块
160
G
B
160GB
160GB的机械硬盘。
1
G
b
p
s
1Gbps
1Gbps的以太网连接,网络拓扑是一个两层的树形结构,根节点的带宽有
100
−
200
G
b
p
s
100-200Gbps
100−200Gbps,所有结点都处于同一域名下,任意两个结点之间的来回通信时间(指不携带数据的情况)不超过一毫秒。
4
G
B
4GB
4GB的内存有
1
−
1.5
G
B
1-1.5GB
1−1.5GB用于其他应用程序。
5.2 Grep
在该项任务中需要扫描
1
0
10
10^{10}
1010条
100
B
100 B
100B的记录,查找目标字符串(目标字符串出现在
92
,
337
92,337
92,337条记录中)。输入文件被分成了
64
M
B
64MB
64MB大小的块(这意味着
M
=
15
,
000
M=15,000
M=15,000)将所有的输出输出到一个文件中(意味着
R
=
1
R=1
R=1)。
下面的Figure 2
展示了随着计算的进行处理数据的速度,横轴代表工作时间,竖轴代表当前时间下处理数据的速度。整个计算过程花了
150
150
150秒左右。
5.3 Sort
一共有
1
0
10
10^{10}
1010条数据需要排序,每条数据的大小为
100
B
100 B
100B。
我们首先了解MapReduce
是如何实现分布式排序的,我们的map
函数需要提取输入文件的每一项记录的用于排序的值作为中间键值对的key
而每一项记录作为中间键值对的value
这样在map
函数执行完成后,
4.1
P
a
r
t
i
t
i
o
n
i
n
g
F
u
n
c
t
i
o
n
4.1\ Partitioning\ Function
4.1 Partitioning Function部分介绍了会进行分组,分组后
4.2
O
r
d
e
r
i
n
g
G
u
a
r
a
n
t
e
e
n
s
4.2\ Ordering\ Guaranteens
4.2 Ordering Guaranteens中介绍了会按照key
进行升序的排序(排序的过程是在reduce
结点拿到了数据后进行也就是执行reduce
的结点进行key
的排序),这样map
的中间输出就是局部有序的。我们的reduce
函数不需要做任何其余的处理,只需要将受到的数据输出到输出文件中,这样每一个输出文件就是有序的。
需要注意的是由于GFS
的实现机制为了保障可用性和可靠性,输出文件会被输出两次,也就是最终需要输出
2
T
B
2\ TB
2 TB的数据。
与Grep
任务一样将输入分成大小为
64
M
B
64MB
64MB的块(
M
=
15
,
000
M=15,000
M=15,000),设置
R
=
4
,
000
R=4,000
R=4,000。
Figure 1
的左边三张图分别展示了:map
的速率,传递中间值的速率,reduce
的执行速率。
从图片中可以看到处理的最大速率比Grep
任务要少,这是因为Sort
的中间文件是与输入文件大小等价的,而grep
的中间文件包含的内容是已经匹配的模式串,该文件的大小是远小于输入文件大小的。同时可以看到在中间数据传递完成后reduce
并没有立即开始写文件而是有一个延迟,这一部分的时间实际上是在对key
进行排序(这也能证明key
的排序是在reduce
方进行的)。
5.4 Effect of Backup Tasks
backup tasks
是
3.6
3.6
3.6部分介绍的,Figure 3
中的中间(竖着)三张图展示了关闭backup tasks
后sort
的各个部分的执行时长。
5.5 Machine Failures
这一部分故意通过kill
命令在几分钟内随机终止
200
200
200个进程(这意味着结点仍然是可用的,只是任务失败了),右边(竖着)三张图展现了这样做sort
的各部分执行时间,可以看到在处理过程中出现了负数速率,这是因为这部分被终止的任务必须要重新执行(速率的计算应该是通过上一时刻的数据量和当前时刻的剩余数据量之差除以时间间隔,由于任务需要重新执行所以当前的数据量增大了,导致速率出现了负数)。
6 Experience
作者团队在完成了MapReduce
的初次实现与优化后,发现该模型适用于很多领域:
- 大规模的机器学习训练;
Google News
和Froogle Products
的聚类问题;- 提取搜索关键字中最常被搜索的词(用于流行词统计);
- 提取能够用于新实验的网页属性;
- 大规模图计算。
Figure 4
展示了基于MapReduce
的应用数量的增长情况。
Table 1
展示了Google
统计的一部分MapReduce
的使用情况数据。
6.1 Large-Scale Indexing
将MapReduce
应用到大规模的索引系统上(指的是搜索引擎的索引,例如使用谷歌搜索后,实际上是一个分布式爬虫,现在需要将各个爬虫的文件中,索引出符合用户关键字的信息)有以下好处:
- 代码编写更加简单,容易理解。因为很多细节的实现都被隐藏了;
- 性能足够好,同时当任务发生改变的时候,能够更快的部署;
- 更容易操作,因为中间的故障处理等都是由底层自动完成的。
7 Related Work
这一部分介绍了一些相关系统。
许多系统已经实现了分布式计算,但是这些方法实现的分布式计算并没有隐藏实现系统,例如故障处理等需要用户参与这一部分,这导致开发成本的上升。
一类同步编程与提供一些原语用于实现分布式编程的系统与MapReduce
的不同在于MapReduce
充分利用了固定的编程模式形式(用户指定map
和reduce
函数)以及让差错处理对用户透明。
本地优化(
3.4
L
o
c
a
l
i
t
y
3.4\ Locality
3.4 Locality)的灵感来自于active disks
。
backup tasks
与Charlotte System
中的渴望调度机制类似。
MapReduce
中的key-sort
与NOW-Sort
中的操作类似。
River
是另一个分布式计算系统,该系统通过精心选择进行通信的结点来提高效率,MapReduce
则是通过固定编程模式来实现分布式计算的简洁性。
BAD-FS
不同于MapReduce
,该系统是做广域网下的分布式计算。即使是这样,两个系统之间也有亮点类似的地方:1)通过重复执行任务来解决结点故障问题;2)通过locality
机制减少数据的发送。
TACC
是一个为了简化高可用网络服务架构的系统。该系统与MapReduce
的相似点是通过重复执行任务来处理结点故障的情况。
8 Conclusions
MapReduce
已经在Google
中的很多任务进行的使用。这些成功可以归结于它具有的以下特点:
- 容易上手:没有分布式编程经验的人也能够很容易地使用
MapReduce
因为其中并行等细节全部是透明的。 - 大量的问题可以被
MapReduce
解决。 - 已经实现了用于大规模分布式的
MapReduce
系统。
能够成功实现MapReduce
并能够广泛使用的原因:
- 限制编程模式使其容易进行并行计算。
- 该系统利用一些机制来节约网络带宽。
- 使用机制减缓了
stragller
的情况。