6.824 分布式系统 MapReduce
2023-10-03 18:15:00
# Papers
总览
MapReduce 是一个编程模型, 也是一个处理和生成超大数据集的算法模型的相关实现
用户需要做的事情:
- 用户指定
Map函数处理一个键值对的集合, 输出基于键值对的中间数据 - 用户指定
Reduce函数把中间数据中具有相同Key的Value值整合起来
MapReduce 需要做的事情: 封装并行计算, 容错, 负载均衡, 数据分布的复杂细节
实现

- 用户程序在主机群中
fork出一个Master程序, 以及多个Worker程序
把原始数据划分成M个数据片段, 决定R个不同分区用来存储中间数据 Master分配任务,M个Map任务和N个Reduce任务- 被分配了
Map任务的Worker程序读入相应的数据片段, 调用Map函数解析为键值对 - 按照用户提供的分区函数 (比如
hash(key) % R) 把键值对放到对应的分区中
把存储位置传回Master, 让Master过后传递给Reduce任务的Worker中 - 被分配了
Reduce任务的Worker程序使用RPC从之前执行Map任务的Worker主机中
对应分区读取数据并排序(可能用到外部排序) - 遍历键值对, 对于相同
Key的键值对, 调用Reduce函数合并数据, 输出到对应分区的输出文件中 - 所有任务完成之后,
Master唤醒用户程序, 调用结束
在最后, 用户可能会把所有 R 个输出文件合并
数据结构
Master
- 存储每个任务的状态(待完成, 完成中, 已完成)
- 对于每个已完成的
Map任务, 存储R个中间文件的大小和位置
容错
Worker错误
Master周期性地ping每个Worker
如果在规定的时间内没有收到返回信息,Master标记失效的Worker
这个Worker的任务被重新设置为空闲状态- 对于该
Worker已完成的Map任务, 需要全部重新完成, 因为Map任务的结果存储在这台主机中.
并且重新执行的动作会被通知给所有的执行Reduce任务的Worker, 从新主机中读取数据 - 对于该
Worker已完成的Reduce任务输出存储在全局文件系统上, 所以不需要重新执行
- 对于该
Master错误
一般有两种办法
- 每隔一段时间把之前
Master存储的所有东西写入硬盘作为一个检查点, 故障时从最近一个检查点恢复 - 重新执行所有任务
故障时语义
当用户定义的 Map 和 Reduce 函数是输入确定性函数(相同输入产生相同输出)时, 那么不会产生语义问题
- 每个
Map任务创建R个临时文件, 完成时把这R个临时文件的信息传给Master
如果Master发现这个任务之前被别的Worker完成了, 那么他忽略这条消息, 否则就记录在数据结构里 - 每个
Reduce任务创建一个临时文件(存放在全局文件系统中), 完成时把它重命名(原子的)
如果多个机器都在执行一个Reduce任务, 他们的重命名请求只在第一次的时候生效
对于非输入确定性的函数 MapReduce 只有较弱的失效处理
存储位置
Master 调度 Map 任务的时候会知道输入文件的位置, 所以尽量在输入文件存储的机器或者附近的机器执行
这样可以减少网络带宽的消耗从而提高整体的效率
任务粒度
Master 执行 $O(M+R)$ 次调度, 内存存储 $O(M \times R)$ 个状态
选择合适的 M 值从而让输入文件被分成 16M ~ 64M 的块被处理(配合存储系统)
选择合适的 R 值, 一般是想使用的 Worker 机器的倍数
任务备份
落伍者指的是对于一台主机, 可能有其他的故障或者任务让它执行 Map 或 Reduce 任务的效率极低, 这会拖延整个任务完成的时间
针对可能出现落伍者的问题, Master 在所有的任务快执行完的时候, 调度备用的 Worker 来处理剩下来的任务
这种策略比正常操作多几个百分点的计算资源, 却能显著减少超大 MapReduce 任务的运行时间
拓展功能
- 分区函数: 用户指定特殊的分区函数来满足特殊要求, 比如把来自同一个主机的 URLs 放在同一个输出文件中
- 有序输出: 在给定的分区中按照
Key值增量排序处理数据 - 合并函数: 用户指定合并函数,
Map结束之后先合并一次再传递给Reduce任务, 可以显著提高一些MapReduce操作的效率 - 输入输出: 用户可以指定输入输出格式
- 跳过故障记录: 如果每次执行一个任务的时候都会出现错误, 在到达一定次数的时候就会忽略这次任务
- 本地运行: 提供本地化运行的
MapReduce, 方便调试 - 使用 HTTP 显示状态信息, 使用计数器统计不同事件发生次数