我目前正在读一篇论文,作者说他们在内存中为每个映射任务都有一些数组,当映射任务结束时,他们会输出该数组。
这是我所指的文件:http://research.google.com/pubs/pub36296.html
这看起来有点不符合mapreduce,但我正在努力实现这个项目,我已经到了一个地步,那就是这是唯一的解决方案。我尝试了很多方法来使用常见的map reduce哲学,即处理每一行并输出一个键值对,但通过这种方式,我对每一行输入都要进行数千次上下文写入,并且需要很长时间才能写入。所以我的地图任务是一个瓶颈。这些上下文编写成本很高。
如果我按照他们的方式来做,我将大大减少键值对的数量。因此,我需要找到一种方法,为每个映射任务提供内存结构。我可以在setup函数中将这些结构定义为静态结构,但我可以找到一种方法来判断映射任务何时结束,这样我就可以输出该结构。我知道这听起来有点奇怪,但这是有效工作的唯一方法。
这就是他们在纸上说的
启动时,每个映射程序都加载要为每个有序属性考虑。对于每个节点n∈n和属性X,映射器维护键的表Tn,X-值对。
在处理完所有输入数据后,映射器输出-放入形式为n、X和值为v、Tn、X[v]的密钥
以下是肖恩回答后的一些编辑:
我在工作中使用组合器。问题是,我的map函数中的context.write(Text,Text)命令确实很耗时。我的输入是csv文件或arff文件。每一行都有一个例子。我的示例可能有多达数千个属性。我为每个属性输出形式为<(n,X,u),Y>,其中是节点的名称(我正在构建决策树),X是属性的名称,u是属性的值,Y是文本格式的一些统计信息。正如您所知,如果我有100000个属性,那么每个示例都必须有100000个context.write(Text,Text)命令。在没有这些命令的情况下运行我的地图任务,它就像风一样运行。如果我添加context.write命令,它将花费很长时间。即使是20万属性训练集。我似乎真的是在文件中写作,而不是在记忆中。所以我真的需要减少这些写作。在内存中聚合它们(在映射函数中,而不是在组合器中)是必要的。
添加了一个不同的答案,因为我现在看到了问题的要点。
要知道映射任务何时结束,可以覆盖close()
。我不知道这是否是你想要的。如果您有50个映射器,则每个映射器看到的输入的1/50是未知的或不能保证的。这对您的用例来说可以吗?您只需要每个工作人员在内存中聚合它所看到和输出的统计数据?
那么您的过程很好,但可能不会使内存中的数据结构static
——没有人说两个Mapper
不会在一个JVM类加载器中运行。
这种模式的一个更常见的版本出现在Reducer
中,在生成一条记录之前,您需要收集一些已知密钥子集的信息。你可以使用一个分区器,以及键被排序的事实,来知道你在一个工作者身上看到了所有的子集,并且可以知道何时完成,因为会出现一个新的不同子集。然后,在处理子集时,很容易在内存中收集数据,输出结果,并在新的子集出现时将其清除
我不确定这是否有效,因为瓶颈发生在Reducer
之前。
如果不了解更多关于输出内容的细节,我无法确定这是否会有所帮助,但这听起来正是组合器的设计初衷。它就像一个附加在Mapper
输出上的小型缩减器(事实上,组合器实现只是Reducer
的另一个实现)。它的目的是收集内存中的映射输出记录,并在将它们写入磁盘并由Reducer
收集之前尝试聚合它们。
典型的例子是对数值进行计数。您可以从映射中输出"key,1",然后在减缩器中将1相加,但是,这涉及到从映射器中输出"key,1"1000次,如果键出现1000次,而"key,1000"就足够了。组合器可以做到这一点。当然,它只适用于所讨论的运算是结合/交换的,并且可以重复运行而没有副作用——加法就是一个很好的例子。
另一个答案是:在Mahout中,我们实现了很多既奇怪又有点复杂的东西,如果用简单的方式完成,速度会非常慢。像在Mapper
中收集内存中的数据这样的技巧是一个次要的,有时也是必要的罪过,所以,它并没有什么错。这确实意味着你真的需要知道Hadoop保证的语义,测试良好,如果不小心的话,还会考虑内存不足的问题。