1

我有一个包含大约 10 亿个数据点的数据集。我想从中提取大约 4600 万个唯一数据点。

我想使用 Hadoop 来提取唯一值,但在 Hadoop 上不断出现“内存不足”和 Java 堆大小错误 - 同时,我可以使用 Python 集(哈希表)在单个盒子上相当轻松地运行它,如果你愿意的话。)

我正在使用一种相当简单的算法来提取这些唯一值:我正在解析地图中的 10 亿行并输出如下所示的行:

UniqValueCount:I    a
UniqValueCount:I    a
UniqValueCount:I    b
UniqValueCount:I    c
UniqValueCount:I    c
UniqValueCount:I    d

然后运行“聚合”reducer 得到结果,对于上面的数据集应该是这样的:

I   4

这适用于一小组值,但是当我为 10 亿个数据点(如我所提到的有 4600 万个键)运行它时,作业失败了。

我在 Amazon 的 Elastic Map Reduce 上运行它,即使我使用六个 m2.4xlarge 节点(它们的最大内存节点为每个 68.4 GB),作业也会因“内存不足”错误而失败。

但是我可以使用 Python 代码在单个 m1.large(一个小得多的具有 8 GB 内存的盒子)上的 Set 数据结构(哈希表)来提取唯一值。我很困惑 Hadoop 作业失败了,因为 4600 万个唯一值不应该占用那么多内存。

可能出了什么问题?我是否使用了 UniqValueCount 错误?

4

1 回答 1

2

您可能会在 shuffle 中遇到内存错误,请记住 Hadoop 在启动 reducer 之前对键进行排序。大多数应用程序不需要排序本身,但 Hadoop 使用它作为聚合属于键的所有值的一种方式。

对于您的示例,您的映射器最终将多次写入相同的值,而您只关心给定键有多少唯一性。这是您现在正在做的事情:

Mapper output:
I -> a
I -> a
I -> a
I -> a
I -> b
I -> a
I -> b

Reducer input:
I -> [a, a, a, a, b, a, b]

Reducer output:
I -> 2

但是在这种情况下你真的不需要写 5*a 或 2*b ,1次就足够了,因为你只关心唯一性。因此,您可以通过确保只发送每个值一次来直接减少大量开销,而不是计算 reducer 中的唯一值:

Mapper output:
I -> a
I -> b

Reducer input:
I -> [a, b]

Reducer output:
I -> 2

这将有效地减少网络带宽,并且洗牌会更简单,因为要排序的键会更少。

你可以通过两种方式做到这一点:

  • 在您的作业中添加一个组合器,它将在映射器之后但在减速器之前运行,并且只会在发送到减速器之前保留唯一性。
  • 修改您的映射器以保留您已发送的映射,如果您之前已经发送过此映射,则不要发送。
于 2013-01-18T17:59:35.517 回答