MapReduce模型
该模型对应的是MapReduce: Simplified Data Processing on Large Clusters
论文中提出的模型。
mr模型基于分布式文件系统和集群,高吞吐但也存在高延迟的大数据处理模型,map和reduce的思想很值得借鉴。
map和reduce任务可以总结为:
-
map:(k1, v1) => list (k2, v2).
-
reduce:(k2, list(v2)) => list(v2).
MR流程
-
将输入文件拆分为大小(由用户定义)相等的M份split,在集群中的每一台机器都fork一份用户代码(用户代码分发),包含master(代码和worker不同)和worker。
-
master有M个map任务和R个reduce任务分配,master从空闲的workers中选出一部分workers做map或reduce任务。
-
map worker读取一个spilt(以流的形式),从中解析出kv对,输入到map函数,得到中间kv对,并将其缓存到内存中。
-
map worker定期将缓存的kv对写入local文件系统,并根据partition函数分成R份,并保存在R个临时输出文件中。当map任务完成时,map worker将R个临时文件在local文件系统的位置发送给master,master将R个文件位置记录到指定的数据结构里,随后将其发送给reduce worker。
-
master通知reduce worker步骤4中的磁盘位置,reduce worker通过远程调用读取数据。读取到所有数据后,reduce worker按key大小进行排序,使得key相同的kv对都在一起。
为什么要排序(一般使用外部排序,数据量太大)?
排序能让key相同的kv对在一块,且由于输出有序,reduce函数的输出也能够很简单的做到有序。
-
reduce遍历kv对中包含的key,将key和其对应的value集合输入到reduce函数中,并将函数输出结果保存到临时输出文件中。(原子操作)当任务完成,reduce worker将临时文件append到该partition的最终输出文件(保存在global文件系统)。
-
当所有的map和reduce任务都完成了,master就会唤醒用户进程(mr是阻塞的),返回R个输出文件。
R个输出文件一般不需要合并到一起,通常会将其作为其他mr任务或分布式程序的输入。
容错机制
论文中,使用心跳机制检查是否故障:每过一段时间,master都会ping每一个worker,若一段时间过后都没有收到回复,将其标记为故障。
不同的机器发生故障,故障恢复的机制也不同。
-
当worker故障:已完成的map任务需要重做,因为map任务的输出是保存在local文件系统,已经无法访问了;已完成的reduce任务无需重做,因为reduce任务的输出保存在global文件系统中。当map worker A故障了,worker B接替A的工作,master会通知所有正在执行reduce任务的worker,从A中没有完全读取到的reduce任务将从B中读取。
-
当master故障:master会定期备份mr过程的数据结构,当master故障后,可以根据上一个检查点备份恢复出一个新的master。但若是一个master,master故障会中止mr,并告知client。
当出现故障时,mr的输出结果是否能保证和没有故障一致?
- 若map和reduce函数都是确定性函数(输入确定,输出也确定),mr可以保证即使出现故障,最终的结果也和没有出现故障的结果一致。
- map worker完成map任务时,会向master发送包含R个临时文件名称的消息,master收到后,若该map任务已经完成时,忽略这条消息,否则,将其记录到数据结构中。
- reduce worker完成reduce任务时,会原子化地将临时文件改名为最终输出文件。当同一个reduce任务由多个机器完成时,会产生多个重命名调用,通过原子化重命名操作,保证结果只包含一个reduce任务的输出。
- 若map和reduce函数都是非确定性函数:因为最终R个文件是经由M个map任务组合而来,当出现故障时,R个文件中的一部分是重做过的,因此不能保证结果和没有故障一致。
任务分配
任务分配给哪台机器
mr任务通常都是在分布式文件系统上运行,在分布式系统中,网络带宽很紧缺。因此,master在分配map和reduce任务时会考虑机器的位置信息,map任务优先分配到包含输入文件副本的机器上,或位置靠近的机器。
任务粒度:M和R多大?
理想情况下,M和R应该尽可能大于机器数量,可以实现更好的动态负载均衡和加速故障恢复。
但M和R也不能太大,master在调度map和reduce worker时,时间复杂度为O(M+R),空间复杂度(map和reduce任务对只需大概一个字节,很小)为O(M*R),用户经常会限制R的大小,因为R个输出文件最终会合并成1个。
M一般设为使单个map任务数据大小为16~64MB的值,R设为机器数量的小倍数(如2,3)。
特定情况下的优化
为适应不同应用场景,mr有一些机制可以进行调整。
备份任务
当一个机器在执行map或reduce任务花了异常长的时间,mr的总体运行时间会被延长。
解决方案:当mr快完成时,master调度几个worker来执行剩余的任务作为备份,当有worker完成时,该任务就标记为完成。
partition函数
为使得map worker在划分输出结果时,保证结果平衡地分布在每一份中,partition函数默认使用hash值(hash_val mod R),但对于特定的结果(如URL),使用其他函数可能效果更好。
combine函数
在某些情况下,map任务会产生很多明显重复的中间kv对,比如word count的map任务,会产生大量key为单词,value为1的kv对,我们可以定义一个combine函数做一部分数据合并的工作。
通常,combine函数和reduce函数相同,唯一的区别就是combine函数的的输出是中间kv对,reduce函数的输出是最终结果。
输入输出类型
mr内置了常见类型的reader,如text格式的文件,一行为一个kv对,key为行号,value为该行的内容。
我们可以自定义reader来使得mr读取一个新的类型。
输出和输入类似,可以通过自定义writer来输出一个新的类型。
额外输出
两阶段提交:可以参考MySQL中binlog和redo log的提交机制。
mr没有设置用于单个任务输出多个文件的两阶段提交,因此若需要在map或reduce任务中生成额外文件,比如用于容错恢复的log,我们需要自己来保证额外输出的正确性,如原子性和幂等性。
跳过错误记录
有些记录会导致map或reduce函数崩溃,若不能修复这个bug且能容忍一部分错误记录,那么mr就会跳过错误记录。
实现:每一个worker都有一个处理错误的handler,当map任务或reduce任务出现错误记录、引发错误时,handler会向master上报,master会记录下该记录标志,当再次发生由该记录引发的错误时,master会在下一次重做时通知worker跳过该记录。
调试
为方便调试,mr的所有环节都可以在本地运行。