用于在大型数据集中对相同值进行分组的高效解决方案

时间:2012-10-15 08:49:29

标签: java algorithm batch-processing spring-batch bigdata

在我的工作中,我将为以下问题开发并实施解决方案:

给定来自特定数据集字段的30M记录提取(键,值)元组的数据集,按键和值对它们进行分组,存储每个键的相同值的数量。将每个密钥的前5000个最常见值写入数据库。每个数据集行包含最多100个(键,值)元组,采用序列化XML格式。

我提出了这样的解决方案(使用Spring-Batch):

批处理作业步骤:

步骤1。迭代数据集行并提取(键,值)元组。获取一些固定数量的元组后,将它们转储到磁盘上。每个元组都转到名为pattern'/ chunk-'的文件,因此指定键的所有值都存储在一个目录中。在一个文件中,值被存储为已排序。

第2步。迭代所有''目录并将其块文件合并为一组相同的值。由于值是按类型存储的,因此将它们合并为O(n * log k)复杂度是微不足道的,其中'n'是块文件中的值的数量,'k'是块的初始数量。

步骤3。对于每个合并文件(换句话说,对于每个键),使用PriorityQueue依次读取其值以保持前5000个值,而不将所有值加载到内存中。将队列内容写入数据库。

我花了大约一个星期完成这项任务,主要是因为我以前没有使用过Spring-Batch,因为我试图强调可扩展性,需要准确实现多线程部分。

问题是我的经理认为这个任务太容易花费那么多时间。

问题是 - 您是否知道更有效的解决方案,或者可能效率更低,更容易实施?您需要多长时间来实施我的解决方案?

我知道类似MapReduce的框架,但是我不能使用它们,因为应用程序应该在一个3核和1GB的Java堆的简单PC上运行。

提前谢谢!

UPD:我想我没有明确表达我的问题。让我以其他方式提出问题:

鉴于问题并且作为项目经理或至少任务审核人,您会接受我的解决方案吗?你会花多少时间来完成这项任务?

4 个答案:

答案 0 :(得分:1)

您确定这种方法比对XML文件进行预扫描以提取所有密钥更快,然后为每个密钥一遍又一遍地解析XML文件吗?您正在此解决方案中执行大量文件管理任务,这绝对不是免费的。

由于你有三个核心,你可以同时解析三个键(只要文件系统可以处理负载)。

答案 1 :(得分:1)

你的解决方案似乎合理有效,但我可能会使用SQL。

解析键/值对时,我会插入/更新到SQL表中。 然后,我会在表格中查询最佳记录。

以下是仅使用T-SQL的示例(SQL 2008,但该概念应该适用于大多数现代rdbms)

/ START /和/ END /之间的SQL将是您需要在代码中执行的语句。

BEGIN
-- database table
DECLARE @tbl TABLE (
    k INT -- key
    , v INT -- value
    , c INT -- count
    , UNIQUE CLUSTERED (k, v)
)
-- insertion loop (for testing)
DECLARE @x INT
SET @x = 0
SET NOCOUNT OFF
WHILE (@x < 1000000)
    BEGIN
    --
    SET @x = @x + 1
    DECLARE @k INT
    DECLARE @v INT
    SET @k = CAST(RAND() * 10 as INT)
    SET @v = CAST(RAND() * 100 as INT)
    -- the INSERT / UPDATE code
    /* START this is the sql you'd run for each row */
    UPDATE @tbl SET c = c + 1 WHERE k = @k AND v = @v
    IF @@ROWCOUNT = 0
        INSERT INTO @tbl VALUES (@k, @v, 1) 
    /* END */
    --
    END
SET NOCOUNT ON
-- final select
DECLARE @topN INT
SET @topN = 50
/* START this is the sql you'd run once at the end */
SELECT 
    a.k
    , a.v 
FROM (
    SELECT 
        ROW_NUMBER() OVER (PARTITION BY k ORDER BY k ASC, c DESC) [rid]
        , k
        , v
    FROM @tbl
) a
WHERE a.rid < @topN
/* END */
END

答案 2 :(得分:0)

哎呀,尝试在记忆中做旧的老式方式似乎并不多。

我会尝试先做,然后如果你的内存耗尽,每次运行尝试一个密钥(根据@ Storstamp的回答)。

答案 3 :(得分:0)

如果由于数据大小而无法使用“简单”解决方案,我的下一个选择是使用SQL数据库。但是,由于大多数内存需要相当多的内存(并且在RAM中严重超载时会崩溃),也许您应该将搜索重定向到NoSQL数据库,例如MongoDB,即使在主要是基于磁盘的。 (您的环境基本上需要,只有1GB的堆可用)。

NoSQL数据库将为您完成所有基本簿记(存储数据,跟踪所有索引,对其进行排序),并且可能比您的解决方案更有效率,因为所有数据都可能在插入时已被排序和索引,删除了对/ chunk-文件中的行进行排序,合并它们等的额外步骤。

您最终会得到一个可能更容易管理的解决方案,它还允许您设置不同类型的查询,而不是仅针对此特定情况进行优化。

作为项目经理,我不反对你目前的解决方案。它已经很快并且解决了这个问题。然而,作为架构师,我会反对,因为解决方案有点难以维护,并且不使用经过验证的技术,这些技术基本上与您自己编码的部分相同。很难打败现代数据库的树和哈希实现。