MapReduce阅读
运行时系统负责对输入数据进行分区、在一组机器上安排程序执行、处理计算机故障以及管理所需的机器间通信的细节。这使得没有并行和分布式系统经验的程序员能够轻松利用大型分布式系统的资源。
编程模型
输入为一组key/value,输出为一组key/value
Map:获取一个输入对,并生成一组中间键/值对。
Reduce:接收一个中间键和该键的一组值,并将这些值合并到一起。
例子
计算字符w的和。
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));
key为文档名,value为文档内容。
map函数计算的就是文档内容里有多少个w,每有一个w就会生成一个(w,1)键值对。
reduce函数则是计算键值对的和。
概念公式
map接收的是k-v,输出的是list(k-v)。reduce处理每个k,即k-list(v),输出聚合的list(v)
实现
Map调用分布在多台机器上,形成M片,Reduce调用也分布在多台机器上,形成R片。
- 输入文件会被分成M片,一片大概16MB到64MB。
- 用户程序会拷贝到不同的计算节点中,节点分为一个master和很多worker,master选择worker执行map或者reduce
- 执行map任务的worker会读入数据,并调用map函数,将计算结果作为中间变量放在memory buffer中。
- 定期将buffer的内容刷到磁盘中,并划分为R个分区。这些buffer所在磁盘的位置信息会传给master,master会将这些位置转发给reduce worker执行reduce操作。也就是说要buffer刷到磁盘中,才能进行后续的reduce
- 在master通知reduce worker这些位置后,reduce worker会把这些磁盘内容拷贝到内存中,然后进行排序,相同key的任务进入到同一个reduce worker中(一个reduce worker可以有多个key),如果数据量大,则需要进行外部排序。
- reduce worker对于每个key,会使用reduce函数进行操作,最后输出到输出文件中。
- 当所有map和reduce任务完成后,master会唤醒用户程序,用户程序从MapReduce调用返回到用户代码。
容错
worker失败
-
master周期性ping每个worker。如果没有收到worker的是消息,则认为该worker失败。
该worker所有已完成的map任务都重新执行
该worker未执行或正在执行的map和reduce任务都重新执行
为什么已完成的map要重新执行而已完成的reduce不用重新执行?
因为map任务完成后存储在磁盘里,如果worker异常,则存储内容的磁盘位置信息丢失。但是reduce任务完成后是存在分布式文件系统的,所以无需重新执行。
-
当map任务worker A中执行完毕后,此时A发生了故障,根据容错处理,worker B重新执行了map任务,则会通知所有的reduce worker这次异常,并让还没有从worker A读取数据的reduce worker从worker B读取数据。
master失败
master上进行周期性check,并记录checkpoint,当master故障后,就从最近的checkpoint进行恢复。
locality
网络传输也是性能瓶颈,所以master会将map任务尽量分配给离所需任务最近的节点。
Task Granularity 任务粒度
map任务和reduce任务的分区粒度是M和R。建议是远大于集群数量,原因:
- 可供每个worker执行的任务有很多选择,从而更好的达到动态平衡。
- 可以加速异常恢复,当worker异常后,它所完成的任务可以分配到大量的worker上并行执行,提高并行速度。(任务分区越多,每个小任务就越小,就可以分到多个worker上进行工作)
通常选择改变M,是的每个每个分区大小在16MB到64MB之间。而R通常受到用户的约束,因为每个reduce任务的输出最终都在一个单独的输出文件中。
Backup Tasks 备份任务
MapReduce操作的总时间取决于“掉队者”:一台机器在执行最后的几个map或者reduce任务耗费了太长的时间,导致了整体时间的拉长。“掉队者”出现的原因有很多,比如这台机器的磁盘损坏,导致读取速度从30MB/s变成1MB/s。比如机器上的其他任务导致MapReduce任务运行缓慢。
如何解决:当MapReduce操作接近完成时,主服务器会安排剩余进行中任务在多个worker中执行,这样有一个执行完毕就算全部执行完毕。
Refinements
Partitioning Function
散列hash函数作为默认分区函数会有相当平衡的分区,不过也可以通过其他分区函数。比如想要相同的url在同一个分区,可以hash(Hostname(urlkey))
Ordering Guarantees
文章保证在给定的分区,中间键值对会按照键增长的顺序进行处理。这种排序保证使每个分区生成排序输出文件变得容易,当输出文件格式需要支持按密钥进行高效随机访问查找,或者输出用户发现数据排序方便时,这非常有用。
Combiner Function
中间键值对是有很多重复的,比如计算字符频次的时候,有很多(w,1)这样的键值对,那么可以在进入reduce之前进行combiner函数,进行部分组合,这样会大大增加速度。
Input and Output Types
MapReduce库支持多种数据格式读入。
Side-effects
在某些情况下,使用者发现如果在map或reduce操作过程中增加辅助输出文件会比较省事,我们依靠程序作者来把这种副作用(side-effects)变成原子的和幂等的。通常应用程序首先把输出结果写到一个临时文件中,在输出全部数据之后,使用原子操作重新命名这个临时文件。
但是如果一个任务产生了多个输出文件,本文并没有提供类似两阶段提交的原子操作来支持这种情况。
Local Execution
提供了一套本地版本的MapReduce库,方便调试。
Stauts Information
master提供了可视化页面,可以监控各种执行状态。
Counters
MapReduce使用计数器统计不同事件发生的次数。