【问题标题】:MapReduce - how do I calculate relative values (average, top k and so)?MapReduce - 我如何计算相对值(平均值、前 k 等)?
【发布时间】:2011-02-19 12:31:59
【问题描述】:

我正在寻找一种在 MapReduce 过程中计算“全局”或“相对”值的方法 - 平均值、总和、顶部等。假设我有一个工人列表,他们的 ID 与他们的工资相关联(以及一堆其他的东西)。在处理的某个阶段,我想知道谁是收入最高 10% 的工人。为此,我需要一些我无法弄清楚的“全局”视图。

如果我将所有值发送到单个 reducer,它具有全局视图,但随后我失去了并发性,这似乎很尴尬。有没有更好的办法?

(我想使用的框架是 Google 的,但我正在尝试找出技术 - 请不要使用特定于框架的技巧)

【问题讨论】:

    标签: hadoop mapreduce average


    【解决方案1】:

    我的第一个想法是做这样的事情:

    MAP:使用一些虚拟值作为键,可能是提高效率的空字符串,并创建同时包含薪水和员工 ID 的类。在每个 Mapper 中,创建一个包含 10 个元素的数组。用你看到的前十个薪水填充它,排序(所以位置 0 是最高薪水,位置 9 是第 10 高)。对于之后的每个薪水,查看它是否在前十名,如果是,则将其插入正确的位置,然后酌情将较低的薪水向下移动。

    Combiner/Reducer:合并排序列表。我基本上会通过创建一个十元素数组来做与映射器相同的事情,然后遍历所有与键匹配的数组,根据与映射器中相同的比较/替换/向下移动序列将它们合并

    如果你用一个 reducer 运行这个,它应该确保输出前 10 的薪水。

    在使用多个减速器时,我看不到有办法做到这一点。如果您使用组合器,那么化简器只需为每个运行映射器的节点合并一个十元素数组(除非您在数千个节点上运行,否则这应该是可管理的)。

    【讨论】:

      【解决方案2】:

      [编辑:我误解了。前 10% 的更新] 对于与“总数”相关的事情,除了先确定总数然后进行计算之外别无他法。

      所以“前10%的薪水”大致可以做如下:

      确定总数:

      1. MAP:身份
      2. REDUCE:汇总所有通过的记录的信息,并使用“total”创建一个新的“特殊”记录。请注意,您想要扩展

      这也可以通过让 MAP 输出 2 条记录(数据、总计)来实现,然后 reducer 通过聚合仅触及“总计”记录。

      使用总量:

      1. MAP:准备 SecondarySort
      2. SHUFFLE/SORT:对记录进行排序,使“总数”的记录首先进入reducer。
      3. REDUCE:根据您的实施,reducer 可能会获得一堆这些总记录(聚合它们),并为所有后续记录确定它们与其他所有记录的关系。

      这种处理方式最大的问题是:意志是规模吗?

      请记住,您正在打破横向扩展的最大“必备”条件:独立的信息块。这使它们依赖于“总”值。 我希望采用一种技术上不同的方法来使第二步的“总”值可用,这对于在“大数据”上进行这项工作至关重要。

      Tom White 的“Hadoop - The definitive Guide”一书中有一个关于二级排序的非常好的章节。

      【讨论】:

      • 谢谢,尼尔斯,但我还是不明白。由于 map 和 reduce 的无状态特性,我根本不知道 10% 的确切限制。即使列表按我要查找的 10% 的值排序,每个减速器都不知道它的部分在完整列表中的位置 - 除非我只使用一个减速器,它确实具有“全局”查看。
      • 嗨,我更新了我的答案,因为我误解了你的问题“前 10 名”!=“前 10%”。尼尔斯
      【解决方案3】:

      我会做这样的事情

      1. 映射器将使用 UUID 作为键的一部分,在映射器的 setup() 方法中创建。映射器作为键发出 UUID 附加 0 或薪水。映射器累积计数和总数。

      2. 在 cleanup() 方法中,映射器发出 UUID 附加 0 作为键,计数和总计作为值。在 map() 方法中,映射器发出 UUID,其后附有薪水作为键,薪水作为值。

      3. 由于键已排序,第一次调用 combiner 时会将 count 和 total 作为值。组合器可以将它们存储为类成员。我们还可以找出总数的 10% 是多少,并将其与班级成员一起保存(称为顶部)。我们初始化一个列表并将其保存为类成员。

      4. 对组合器的后续调用将包含薪水作为值,按排序顺序到达。我们将值添加到列表中,同时增加一个计数器。当计数器达到值顶部时,我们不再在列表中存储任何值。我们忽略其余组合器调用中的值。

      5. 在组合器 cleanup() 中,我们执行发射。组合器将仅发出 UUID 作为键。该值将包含计数和总计,然后是前 10% 的值。所以组合器的输出将有部分结果,基于通过映射器的数据子集。

      6. 在这种情况下,reducer 将被调用的次数与映射器的数量一样多,因为每个映射器/组合器只发出一个键。

      7. reducer 将在 reduce() 方法中累积计数、总数和前 10% 的值。在 cleanup() 方法中,计算平均值。前 10% 也是在 cleanup() 方法中从在 reducer 的每次调用中到达的前 10% 的聚合中计算出来的。这基本上是一种归并排序。

      8. reducer cleanup() 方法可以进行多次发射,因此平均值位于第一行,随后是前 10% 的工资。

      9. 最后,为了确保最终的聚合统计信息是全局的,您必须将 reducer 的数量设置为 1。

      10. 由于reducer中有数据的积累和排序,虽然在部分数据集上,可能存在内存问题。

      【讨论】:

        猜你喜欢
        • 2021-07-03
        • 2022-11-29
        • 2015-02-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-07
        相关资源
        最近更新 更多