当k太大而无法在内存中拟合k个元素时,从数据集中查找top-k元素的有效MapReduce算法是什么?我说的是数百万个元素的数据集,而k是例如其中3/4。想象一下,每个元素都有一个值,我们想要找到具有最高值的k个元素。
E.g。表格中的数据:
e1:5
e2:10
e3:7
e4:8
然后,前2名是e4和e2(不关心他们的相关顺序)。
我见过the solution to the problem, when k is small enough,但它没有扩展。 显然,使用单个减速器,再次不实用(内存不足错误)。
答案 0 :(得分:1)
您需要两种MR工作方式:
第一份工作:
在映射器中执行您描述的逻辑以在reducer中获取分组计数。 然后,reducer将计数(作为键)写入键值对(作为值)。这里的reducer可以并行化,以防你遇到性能问题。
第二份工作:
映射器只是映射身份。通过定义倒置比较器来处理降序排序。
此处的单个reducer获取降序排序数据。 然后你可以简单地增加,直到你击中" k"并发出值。
请注意,您可能拥有相同计数的项目,因此您需要将从减少的值中获得的每个值计为新的" k"。
答案 1 :(得分:0)
这可能不是 最有效,但它易于理解且易于实施。
MapReduce Stage-1: 将减速器数量设置为1。
MapReduce Stage-2:不需要减少阶段。
如果要选择k为百分比的前k,则可以在Stage-1映射阶段使用Hadoop counter来计算输入文件中存在的记录数,然后在其中使用另一个计数器Stage-2选择前k%。
答案 2 :(得分:0)
我认为我找到了我想要的东西。答案在这里找到: http://www.philippeadjiman.com/blog/2009/12/20/hadoop-tutorial-series-issue-2-getting-started-with-customized-partitioning/
想法是使用TotalOrderParitioner。此分区器首先需要采样,可以使用InputSampler生成,例如RandomSampler。我相信,此采样用于负载平衡,以确保所有减速器将获得几乎相同的工作量(数据)。
默认分区程序(hashPartitioner)的问题在于,(键,值)对最终的reducer基于键的哈希值。然后,排序发生在每个减速器的输入中。这并不能保证“跟随”减速器可以处理更大的密钥。 TotalOrderPartitioner保证后者,并且采样用于负载平衡。
数据完全排序后,我们可以取最后一个k(例如,在tail -k
的结果中使用unix中的hadoop dfs -getmerge
命令),或者使用倒置比较器并取正如Thomas Jungblut所暗示的那样,第一个k。如果不正确,请随意评论/编辑我的答案。
编辑:提供了一个更好的示例(就源代码而言)here。
编辑2:看起来这个问题毕竟是一个“经典”问题,汤姆怀特的书“Hadoop the Definitive Guide”(第1版第223页)的“总排序”一节中也描述了这个解决方案。 。您也可以关注this link进行免费预览。