我正在寻找一种在MapReduce过程中计算“全局”或“相对”值的方法 - 平均值,总和,顶部等。说我有一个工人列表,其ID与他们的工资相关(和一堆其他东西)。在处理的某个阶段,我想知道谁是赚取前10%工资的工人。为此我需要一些“全局”的价值观,这是我无法弄清楚的。
如果我将所有值都发送到单个reducer中,它就具有该全局视图,但随后我松散并发,这看起来很尴尬。还有更好的方法吗?
(我想使用的框架是Google的,但我正在试图找出这种技术 - 请不要使用框架特定的技巧)
答案 0 :(得分:0)
答案 1 :(得分:0)
我的第一个想法是做这样的事情:
MAP:使用一些虚拟值作为键,可能是空字符串以提高效率,并创建包含工资和员工ID的类。在每个Mapper中,创建一个包含10个元素的数组。填写你看到的前10个工资,排序(所以位置0是最高工资,位置9是第10个最高)。对于之后的每个薪水,看看它是否在前十名中,如果是,请将其插入正确的位置,然后根据需要向下移动较低的薪水。
Combiner / Reducer:合并排序列表。通过创建一个十元素数组,然后循环遍历所有与键匹配的数组,并根据与映射器中相同的比较/替换/下移顺序将它们合并,我基本上和映射器一样做同样的事情
如果使用一个减速器运行它,则应确保输出前10名工资。
在使用多个reducer时,我没有看到这样做的方法。如果使用组合器,则reducer应该只为每个运行映射器的节点合并一个十元素数组(除非你在数千个节点上运行,否则应该是可管理的)。
答案 2 :(得分:0)
我会做这样的事情
映射器将使用UUID作为密钥的一部分,在映射器的setup()方法中创建。映射器作为键发出,UUID附加0或工资。映射器累计计数和总数。
在cleanup()方法中,映射器发出UUID,后面附加0作为键,count和total作为值。在map()方法中,映射器发出附加了salary作为键的UUID和作为值的salary。
由于密钥已排序,因此对组合器的第一次调用将使用count和total作为值。组合器可以将它们存储为类成员。我们还可以找出总数的10%,并保存该类成员(称之为顶部)。我们初始化一个列表并将其保存为类成员。
对combiner的后续调用将包含薪水作为值,按排序顺序到达。我们将值添加到列表中,同时递增计数器。当计数器达到顶部值时,我们不再在列表中存储任何值。我们忽略其余组合器调用中的值。
在组合器清理()中,我们执行emit。组合器将仅发出UUID作为密钥。该值将包含count和total,后跟前10%的值。因此,基于通过映射器的数据子集,组合器的输出将具有部分结果。
在这种情况下,reducer将被调用多次,因为每个mapper / combiner只发出一个键。
reducer将累积reduce()方法中的计数,总计和前10%的值。在cleanup()方法中,平均值被计算。前10%也是在cleanup()方法中根据每次调用reducer的前10%的汇总计算的。这基本上是一种合并排序。
reducer cleanup()方法可以执行多次发射,因此平均值位于第一行,后面是后续行中前10%的工资。
最后,为了确保最终的汇总统计数据是全局的,您必须将减速器的数量设置为1。
由于在reducer中存在数据累加和排序,但在部分数据集上,可能存在内存问题。