传统MapReduce的特点:一个job只有一个map-reduce对,map完成之后将结果写入本地磁盘,所有的map任务都完成之后,reduce才开始执行,需要网络I/O传输数据,reduce执行完成之后将结果写入Hdfs。
迭代计算的特点:
一、input data由两部分组成,即static和variable数据,而且大部分情况下static数据比variable数据量大很多,static与variable数据完成计算之后,得到新的variable数据,然后该新数据与static数据再次进行计算,如此循环,直到迭代结束条件得到满足之后才退出。
二、迭代计算单个任务的数据集普遍不是很大。
修改传统mapreduce使之适应迭代计算的方向:
1.修改原有的job结构,使之能运行多个map-reduce对,这样就不用启动多个job(迭代计算的循环体在job内发生,即job复用),job复用可以大大迭代效率;
2.map/reduce静态数据缓存(常驻内存或者cache进磁盘,),让map任务可以快速进入计算,
但是这必须要scheduler的配合,即计算locality;
3.reduce之后进行处理,继而判断迭代是否结束,避免额外的任务消耗。
该文章提出的也是一个增强版的MapReduce,使之能够适用于聚类分析,机器学习或计算机视觉处理等需要迭代计算的应用中。架构图如下:
二、输入数据(map)
对map任务Twister提供了两种输入数据的方式:
架构说明:
Twister Driver——master:负责数据和任务的分发;
Broker Network:Twister的消息传递机制;
Twister Daemon——slave:task处理单元,处于worker node上。
很明显的master/slave架构,两者之间用Broker network通信(跟Hadoop的RPC通信有所不同),采用Pub/Sub消息传递机制服务以下四种需求:
a.传递控制事件(control events);
b.Twister driver向Twister daemon传递数据(这个主要是传递variable迭代数据供下一轮迭代);
c.map/reduce之间传递中间结果;
d.reduce完成之后(
加了一个combine过程,其实就是一个合并,方便判断迭代结束与否)结果传回Twister driver。
修改内容:
一、循环体控制
上面已经提出,要使得迭代效率提高,必须把循环体控制在job内,下图就表示了如何做到这一点:
a.从本地磁盘读取;
b.从broker network中获取。
对于第一种方式,用户可能需要自己对文件进行切分,Twister没有提供自动切分的接口,但是给出了分区文件(partition file,受微软的DryadLINQ的启发)的概念,用户可以自己定义切分,partition file中会予以记录。
对于第二种方式,可能不太适用于大量数据的传输,比如初次迭代,这时候就不能用这种方式,但是,对于variable data的传输却很有用,比如reduce传给Twister driver的迭代结果供下次使用时,就可以通过broker network传输。
三、任务调度(task scheduling)
上面已经说明,数据缓存只有在scheduler的配合下才有用,否则效率就很低,比如同一work node上的两次map任务的针对的key如果不同,那么上次的缓存就可能毫无意义。因此,Twister采用的是静态分配的方式。
容错方面:
不得不说的是Twister的容错方案很不理想,它首先就示弱,说容错基于以下的三个假设:
a.master宕掉的几率可以忽略不计(
这其实是很可笑的);
b.消息传递的容错独立于Twister之外(
我的理解是它完全信任Pub/Sub,这个我问过我师兄,对于Twister现在所用的树型消息传递机制这里也是不可忽略的);
c.数据在计算框架内复制传递(这是很明显的)。
因为上面已经介绍在所有reduce完成之后Twister又加了个combine的过程,用以判断迭代结束条件是否满足,在这里把它当做一个全局同步点(global barrier),同时,Twister把每次部署map/reduce的configuration连同input data一起保存下来,如果在同步点发现了任务丢失,就简单地重新开始走一遍,如果找不到数据的话就算了,这个recovery操作就此终止(多么扯淡。。。)。
总结Twister:
Twister在功能上基本上完成了对传统mapreduce的修改以适用于迭代计算,包括job复用,数据缓存,迭代结束条件判断等,但是在性能方面应该还有很多缺陷,上面已经做了说明。
参考文献: