使用Java处理大量数据

使用Java处理大量数据,java,database,Java,Database,作为需求的一部分,我们需要处理近300万条记录,并将它们与一个存储桶关联。这种关联取决于一组规则(由5-15个属性组成,具有单个或范围的值和优先级),这些规则派生记录的存储桶。 如此大数量的顺序处理显然超出了范围。 有人能指导我们有效设计解决方案的方法吗 我不太确定您想要的是什么。从数据量的角度来看,300万条记录并不是那么多(显然取决于记录大小),因此我建议最简单的尝试是跨多个线程并行处理(使用java.util.concurrent.Executor框架)。只要有多个CPU核可用,您就应该能

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

我不太确定您想要的是什么。

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

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

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


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

是否有必要使用Java来处理数据?不能使用SQL查询写入中间字段吗?您可以在每个字段(属性)的基础上进行构建,直到您拥有了所需的所有内容


或者你可以使用SQL和java的混合体。。。使用不同的过程获取不同的“存储桶”信息,然后将其发送到一个线程路径以进行更详细的处理,然后使用另一个查询获取另一组数据并发送到另一个线程路径…

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

第二个明显的问题是,您在哪里获取信息,在本例中,您提到了一个数据库,但实际上这是非常不相关的。您希望在代码中分离I/O和处理元素,以分离线程(或者更可能是处理的执行器池)

尽量使每一个都独立,并记住在必要时使用锁定。这里有一些链接,你可能想读一下



此场景的有效设计步骤包括:首先,确定可以对要处理的记录进行分区的任何和所有位置,以允许完全引擎并行化(即,四个单元运行750k记录,每个单元相对便宜)。 然后,根据汇总记录的规则的成本(我将bucket的分配视为汇总操作),确定您的操作是受CPU约束还是受记录检索约束

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

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

这样一个大数据的顺序处理 这个数字显然超出了范围

我想你不知道。以这种方式处理1000条记录需要多长时间?10,000? 100,000? 1,000,000? 如果答案真的是“太长了”,那么很好:开始寻找优化。但你可能会发现答案“无关紧要”,然后你就完蛋了


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

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

这是一个毫无意义的基准,但它是一个实例,根据环境的不同,3M行在今天的硬件上并不是很多行

也就是说

正如其他人所建议的,将作业分解为若干部分,并并行运行,每个核心1-2个线程。每个线程维护自己的本地数据结构和状态,最后,主进程合并结果。这是一个粗糙的“map/reduce”算法。这里的关键是确保线程不会争夺全局资源,如全局计数器等。让线程结果的最终处理以串行方式处理这些资源

如果每个线程都在执行DBIO,那么每个核心可以使用多个线程,因为没有一个线程是纯CPU绑定的。只需使用不同的线程数运行该进程几次,直到结果最快

我们已经看到了50%的速度提高,甚至当我们通过一个持久的排队系统(如JMS)运行批处理来分配工作与线性处理时,我已经在2核笔记本电脑上看到了这些进步,因此这里有一定的进步空间

如果可能的话,另一件事是不要执行任何磁盘IO(保存从磁盘读取数据)
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
Worker worker = new Worker(bucketID);
worker.doWork();
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