用大数据计算公共组成员资格的算法

时间:2013-04-05 09:42:47

标签: java sql algorithm postgresql bigdata

我需要编写一个程序来计算两个用户在同一组中的次数。用户按用户名和组ID分配。 例如,使用输入(存储在文本文件中):

john 32
john 21
jim 21
jim 32
bob 32

我想要结果:

john-jim 2 
john-bob 1
jim-bob 1

这听起来微不足道。但问题是:我有1,800万组和30万用户。还有很多会员资格(我预计每个用户平均至少50个,可能更多)。这意味着需要大量的数据和处理。

我写了5个不同的程序,没有一个能够减少数据量:它像PostgreSQL查询一样慢。耗尽内存消耗在Java工作内存中的Map中运行(第一个堆空间,在优化之后我得到了罕见的“超出GC开销限制”)。从Java连续写入数据库太慢(即使使用批处理查询进行优化)。越来越绝望,我尝试了一些更奇特的东西,比如把所有的对都写成一个数组,然后对它们进行排序(O(n log(n)))然后将它们计算为peuàpeu。但是仍然有太多的数据存储在内存中。

有关算法的任何想法吗?或者这是不可能的?

3 个答案:

答案 0 :(得分:7)

RDBMS专门用于排序等操作。在数据库外部执行此操作几乎不会在性能上接近。用SQL做吧!

这可以完成工作(在更新中简化​​):

SELECT t1.usr || '-' || t2.usr, count(*) AS ct
FROM   usr_grp t1
JOIN   usr_grp t2 USING (grp_id) 
WHERE  t2.usr > t1.usr   -- prevent dupes and get sorted pair
GROUP  BY t1.usr, t2.usr;

正如您所说,根据您拥有的重叠次数,这可能会产生大量的行。所以这永远不会很快。

提出问题:生成无人可以处理的数百万行的目的是什么?你确定,这个操作从一开始就有意义吗?

为了让它更快,你可以......

  • 升级! PostgreSQL 8.4 is rather outdated by now。特别是,PostgreSQL 9.2专注于大数据。对于像这样的工作,你可以期待更多更好的表现 并且 nobody 应该运行8.4.0。仅出于安全原因,您也错过了很多错误修复。目前的释放点是8.4.17。我引用链接的网站:
  

我们始终建议所有用户运行最新的可用次要内容   适用于任何主要版本的版本。

  • 使用 integer 作为用户的代理键,因此您只能在usr_grp处理整数。使表和索引更小并且处理更快。如果n:m表(usr_grp)的基数比表usr大得多,那么它应该更快,即使它意味着额外的连接。

SELECT u1.usr  || '-' || u2.usr, count(*) AS ct
FROM   usr_grp t1
JOIN   usr_grp t2 USING (grp_id) 
JOIN   usr u1 ON t1.usr_id = u1.usr_id
JOIN   usr u2 ON t2.usr_id = u2.usr_id
WHERE  t2.usr_id > t1.usr_id
GROUP  BY u1.usr_id, u2.usr_id;

    CREATE INDEX usr_grp_gu_idx ON usr_grp(grp_id, usr_id);

测试用例

我将数字@OldCurmudgeon reported用于他的测试用例并在PostgreSQL中创建了一个类似的测试用例。

-> SQLfiddle demo.

此公共测试数据库中

250 ms 结果未订购(无ORDER BY),因为尚未指定 与 2.5分钟相比,reported below。因素600。

答案 1 :(得分:2)

如何让文件系统执行此操作。

对于每个条目 - 打开一个以组ID命名的文件并附加新用户的名称。每组最终会有一个文件。

你现在有 - 例如:

Group-21.txt
 jim
 john

Group-32.txt
 bob
 jim
 john

现在遍历所有文件,在其中生成每个用户名对(我会对名称进行排序并对它们执行标准组合过程)。对于每对,请将“1”附加到具有特定名称的文件。

你现在有 - 例如:

User-jim-john.txt
 11

User-bob-jim.txt
 1

User-bob-john.txt
 1

现在,文件名和计数对(在一元中,所以你真正需要的是文件大小,以字节为单位)。

几乎所有这些都可以并行完成,但第1阶段必须在第2阶段开始之前完成。为了提高速度 - 添加核心 - 购买更快的磁盘。没有内存限制,只有磁盘。

已添加:我刚刚使用一个线程对此算法运行了一些模拟测试

1800个组,300个用户和15000个成员资格都是随机生成的,大约需要2.5分钟。 900个团体,150个用户和7500个会员资格需要54秒。

答案 2 :(得分:1)

无论解决方案是什么,复杂性取决于生成的对的数量,而不一定取决于组或人的数量。对于不同的团体规模:

  • 一个有10名成员的小组产生C(10,2)= 45对
  • 一个有100名成员的小组产生C(100,2)= 4950对
  • 一个有1000名成员的团体,499500对...
  • 拥有10000名会员,单个团体将产生近5000万双!因此,单个组可以对其余计算的全部成本进行加权。

所以我的第一个建议是在数据集中清除非常大的组。如果您不能省略大型组,并发现它不适合内存或者需要花费很长时间才能通过单个线程进行操作,则可以使用Map-Reduce自动并行化计算,如下所示。如果您从组成员身份开始,例如:

32 -> john, jim, bob
21 -> john, jim

您可以使用地图步骤生成所有对:

john-jim -> 32, john-bob -> 32, jim-bob -> 32
john-jim -> 21

这些将通过名称对聚合。然后在reduce中,只计算每个键的出现次数。这假设您有足够的磁盘来存储所有对。