Google MapReduce 总结
MapReduce 编程模型
总的来讲,Google MapReduce 所执行的分布式计算会以一组键值对作为输入,输出另一组键值对,用户则通过编写 Map 函数和 Reduce 函数来指定所要进行的计算。
由用户编写的Map 函数将被应用在每一个输入键值对上,并输出若干键值对作为中间结果。之后,MapReduce 框架则会将与同一个键 II 相关联的值都传递到同一次 Reduce 函数调用中。
同样由用户编写的 Reduce 函数以键 II 以及与该键相关联的值的集合作为参数,对传入的值进行合并并输出合并后的值的集合。
形式化地说,由用户提供的 Map 函数和 Reduce 函数应有如下类型:
map(k1,v1)→list(k2,v2)reduce(k2,list(v2))→list(v2)map(k1,v1)→list(k2,v2)reduce(k2,list(v2))→list(v2)
值得注意的是,在实际的实现中 MapReduce 框架使用 Iterator
来代表作为输入的集合,主要是为了避免集合过大,无法被完整地放入到内存中。
作为案例,我们考虑这样一个问题:给定大量的文档,计算其中每个单词出现的次数(Word Count)。用户通常需要提供形如如下伪代码的代码来完成计算:
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));
函数式编程模型
了解函数式编程范式的读者不难发现,MapReduce 所采用的编程模型源自于函数式编程里的 Map 函数和 Reduce 函数。后起之秀 Spark 同样采用了类似的编程模型。
使用函数式编程模型的好处在于这种编程模型本身就对并行执行有良好的支持,这使得底层系统能够轻易地将大数据量的计算并行化,同时由用户函数所提供的确定性也使得底层系统能够将函数重新执行作为提供容错性的主要手段。
MapReduce 实现
计算执行过程
每一轮 MapReduce 的大致过程如下图所示: