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



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

给定一个由30M条记录组成的数据集,从特定的数据集字段中提取(键,值)元组,按键和值对它们进行分组,为每个键存储相同值的数量。将每个键的前5000个最频繁值写入数据库。每个数据集行最多包含100个(键、值)元组,这些元组采用序列化XML的形式。

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

批处理作业步骤:

步骤1.遍历数据集行并提取(键、值)元组。在得到一些固定数量的元组后,将它们转储到磁盘上。每个元组都指向一个名称模式为"/cochunk-"的文件,因此指定键的所有值都存储在一个目录中。在一个文件中,值被排序存储。

步骤2.遍历所有"目录,并将它们的区块文件合并为一组相同的值。由于值是按排序存储的,因此对于O(n*log k)复杂性,合并它们是很简单的,其中"n"是块文件中的值的数量,"k"是块的初始数量。

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

我花了大约一周的时间来完成这项任务,主要是因为我以前没有使用过SpringBatch,而且我试图强调需要准确实现多线程部分的可伸缩性。

问题是我的经理认为这项任务太容易了,不能花那么多时间。

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

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

提前谢谢!

乌干达人民国防军:我想我没有明确说明我的问题。让我换一种方式问:

考虑到这个问题,作为项目经理或至少是任务审查员,你会接受我的解决方案吗?你会为这项任务投入多少时间

您确定这种方法比预扫描XML文件以提取所有键,然后为每个键反复解析XML文件更快吗?您在这个解决方案中执行了许多文件管理任务,这绝对不是免费的。

由于您有三个Core,您可以同时解析三个密钥(只要文件系统能够处理负载)。

您的解决方案看起来合理高效,但我可能会使用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

Gee,尝试在内存中执行这项操作的老式方式似乎不需要太多工作。

我会先尝试一下,然后如果内存不足,每次运行一个键(根据@Storstamp的回答)。

如果由于数据的大小,使用"简单"解决方案不是一个选项,那么我的下一个选择将是使用SQL数据库。然而,由于其中大多数都需要相当多的内存(当RAM严重过载时,会导致爬行),也许你应该将搜索重定向到像MongoDB这样的NoSQL数据库中,即使在大部分基于磁盘的情况下,它也会非常高效。(这是您的环境基本上需要的,只有1GB的堆可用)。

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

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

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

相关内容

最新更新