使用Java处理大量数据

时间:2009-06-12 13:59:59

标签: java database

作为要求的一部分,我们需要处理近300万条记录并将其与存储桶相关联。此关联由一组规则决定(包括5-15个属性,具有单个或一系列值和优先级),这些规则派生记录的存储桶。 如此大数量的顺序处理显然超出了范围。 有人可以指导我们有效设计解决方案的方法吗?

10 个答案:

答案 0 :(得分:6)

从数据量的角度来看,300万条记录并不是那么多(显然取决于记录大小),所以我建议最简单的尝试是跨多个线程并行处理(使用java.util.concurrent.Executor框架)。只要您有多个可用的CPU内核,就应该能够获得接近线性的性能提升。

答案 1 :(得分:3)

这取决于数据源。如果它是单个数据库,您将花费大部分时间来检索数据。如果它位于本地文件中,则可以将数据分区为较小的文件,或者可以将记录填充为具有相同的大小 - 这允许随机访问一批记录。

如果您有多核计算机,则可以并行处理分区数据。如果确定了记录桶分配,则可以使用PreparedStatement的批处理功能将信息写回数据库。

如果您只有一台核心机器,您仍然可以通过设计数据检索 - 数据处理 - 批量回写分离来获得一些性能改进,以利用I / O操作的暂停时间。

答案 2 :(得分:1)

答案 3 :(得分:1)

作为一个毫无意义的基准,我们有一个具有内部缓存的系统。我们目前正在加载500K行。对于每一行,我们生成统计信息,将密钥放在不同的缓存中等。目前,这需要< 20秒供我们处理。

这是一个毫无意义的基准测试,但它是一个实例,根据具体情况,3M行在今天的硬件上并不是很多行。

那就是说。

正如其他人所建议的那样,将工作分解成碎片并并行运行,每个核心1-2个线程。每个线程都维护自己的本地数据结构和状态,最后,主进程合并结果。这是粗略的“map / reduce”算法。这里的关键是确保线程不会像全局计数器等那样争夺全局资源。让线程结果的最终处理顺序处理。

如果每个线程正在执行DB IO,则每个核心可以使用多个线程,因为没有一个线程纯粹是CPU绑定的。只需使用不同的线程计数多次运行该过程,直到它出现最快。

我们已经看到50%的速度提升,即使我们通过像JMS这样的持久排队系统运行批处理来分配工作与线性处理,我已经在2台核心笔记本电脑上看到了这些增益,因此有明确的空间在这里取得进展。

如果可能,另一件事是不要做任何磁盘IO(保存从DB读取数据)直到最后。此时,您有更多机会批量处理需要进行的任何更新,这样您至少可以减少网络往返时间。即使您必须更新每一行,大批量的SQL仍将显示性能的净增益。显然这可能是内存密集型的。值得庆幸的是,大多数现代系统都有很多内存。

答案 4 :(得分:0)

您是否有必要使用Java来处理数据?你不能用SQL查询写入中间字段吗?您可以构建每个字段 - 属性 - 直到您拥有所需的所有内容。

或者您可以使用SQL和Java的混合...使用不同的过程来获取不同的“桶”信息,然后将其发送到一个线程路径以进行更详细的处理,并将另一个查询发送到另一组数据并发送沿着不同的线程路径......

答案 5 :(得分:0)

对于需要处理大量信息的大多数项目,情况都是如此。我假设每条记录是相同的,例如你每次都以相同的方式处理它,这将是你可以产生一个单独的线程来进行处理的点。

第二个明显的问题是你要获取你的信息,这个案例你提到了一个数据库,但实际上这是非常无关紧要的。您希望将代码中的I / O和处理元素分离为单独的线程(或者更可能是用于处理的执行程序池)。

尽量让每个人尽可能独立,并记得在必要时使用锁定。以下是您可能想要阅读的一些链接。

http://www.ibm.com/developerworks/library/j-thread.html
http://www.ibm.com/developerworks/java/library/j-threads1.html http://www.devarticles.com/c/a/Java/Multithreading-in-Java/

答案 6 :(得分:0)

此方案的有效设计步骤包括:首先,确定可以对要处理的记录进行分区的任何和所有位置,以允许全引擎并行化(即,针对750k记录运行的四个单元各自相对便宜)。 然后,根据汇总记录的规则的成本(我正在查看存储桶的分配作为汇总操作),确定您的操作是CPU绑定还是记录检索绑定。

如果您受CPU限制,增加分区是您获得的最佳性能。如果你是IO绑定的,那么可以并行工作以响应分块数据检索的规则处理工作线程是一种性能更好的设计。

所有这些都假定您的规则不会导致需要在记录之间跟踪的状态。这种情况严重威胁了并行化方法。如果并行化不是一个易处理的解决方案,因为累积状态是规则集的一个组成部分,那么您的最佳解决方案实际上可能是对单个记录的顺序处理。

答案 7 :(得分:0)

  

如此大的顺序处理   数字明显超出范围。

我认为你不知道。以这种方式处理1,000条记录需要多长时间?万? 100000?百万?如果答案真的“太长了”,那么很好:开始寻找优化。但你可能会发现答案是“微不足道的”,然后你就完成了。

其他答案也提到了这一点,但这是我的全部答案。在开始优化之前证明您有问题。然后你至少得到了一个简单,正确的系统来分析和比较优化的答案。

答案 8 :(得分:0)

根据修改后的描述,我想我会尝试对数据进行排序。

排序可以是n log(n)过程;如果大多数比较是针对可排序字段的直接相等,则应该产生~O(n log(n))的总复杂度。理论上。如果在将项目分配给存储桶后不再需要,只需将其从数据列表中删除即可。

即使数据需要在逻辑中的各个步骤中使用几次,它仍然应该比n ^ 2方法快一点。

基本上,这将涉及预处理数据,以便更容易进行实际处理。

这对存储桶分配的逻辑做了一些假设(nameley认为它与提供的伪代码相差不远);如果你需要从每对A,B中提取数据,那么它将无效。

希望这有帮助。

编辑:如果可以,我会评论;但是,唉,我太新了。预处理同样适用于数据,也适用于各个类别。最终,从15分钟的计算时间到5分钟的计算时间,您需要做的就是能够以编程方式确定不能和将永远不会匹配的2 / 3s +类别......在少于O(n)的摊销中时间。我承认,这可能不适用于您的具体情况。

答案 9 :(得分:0)

我会努力推动规范作者更多地关注“需要做什么”,而不是如何做。我无法想象为什么一个规范会推动'java'进行数据密集型操作。如果它与数据有关,请使用SQL。如果您使用Oracle,则会有一个名为nTile的函数。因此,创建一组固定的存储桶同样简单:

选择ntile(4)over(order by empno)grp,  EMPNO,  为ename 来自emp

结果是:

GRP EMPNO ENAME
--- ----- ---------
1  7369 SMITH
1  7499 ALLEN
1  7521 WARD
1  7566 JONES
2  7654 MARTIN
2  7698 BLAKE
2  7782 CLARK
2  7788 SCOTT
3  7839 KING
3  7844 TURNER
3  7876 ADAMS
4  7900 JAMES
4  7902 FORD
4  7934 MILLER

至少你可以在SQL中建立你的'桶',然后你的Java代码只需要处理一个给定的桶。

Worker worker = new Worker(bucketID);
worker.doWork();

如果你不关心桶的数量(上面的例子是要求4个桶),而是每个桶的固定大小(每桶5个记录),那么SQL是:

select ceil(row_number()over(order by empno)/5.0) grp,
  empno,
  ename
from emp

输出:

GRP      EMPNO ENAME
    --- ---------- -------
1       7369 SMITH
1       7499 ALLEN
1       7521 WARD
1       7566 JONES
1       7654 MARTIN
2       7698 BLAKE
2       7782 CLARK
2       7788 SCOTT
2       7839 KING
2       7844 TURNER
3       7876 ADAMS
3       7900 JAMES
3       7902 FORD
3       7934 MILLER

以上两个例子都来自这本极好的书: SQL Cookbook,Anthony Molinaro第1版