1

我有一个要求,我必须从数据库中选择大约 6000 万条记录。一旦我在 ResultSet 中拥有所有记录,那么我必须根据客户要求(日期格式和数字格式)格式化一些列,然后我必须将所有记录写入文件(辅助内存)。

  • 目前我正在从数据库中每天选择记录(7 天选择 7 次)并将它们放入 HashMap 中。从 HashMap 读取并格式化一些列,最后写入一个文件(单独的文件 7 天)。
  • 最后,我将所有 7 个文件合并到一个文件中。

  • 但这整个过程需要6个小时才能完成。为了改进这个过程,我创建了 7 个线程 7 天,所有线程都在编写单独的文件。

  • 最后,我将所有 7 个文件合并到一个文件中。这个过程需要2个小时。但我的程序将在 1 小时后进入 OutOfMemory 等。

请为这种情况提出最佳设计,我是否应该使用一些缓存机制,如果是,那么是哪一种以及如何使用?

注意:客户不想更改数据库中的任何内容,例如创建索引或存储过程,他们不想接触数据库。提前致谢。

4

4 回答 4

4

您是否需要将所有记录都保存在内存中才能格式化它们?您可以尝试通过流程和对文件的权限来流式传输记录。如果您甚至能够进一步分解查询,您就可以开始处理结果,同时您仍然可以检索它们。

根据您的数据库后端,他们可能有工具来帮助解决这个问题,例如用于 Sql Server 2005+ 的 SSIS。

编辑

我是一名 .net 开发人员,所以让我建议我将在 .net 中做什么,并希望您可以在 java 端转换成类似的技术。

ADO.Net 有一个 DataReader,它是结果集的只进、只读 (Firehose) 游标。它在查询执行时返回数据。这个非常重要。本质上,我的逻辑是:

IDataReader reader=GetTheDataReader(dayOfWeek);

while (reader.Read())
{
    file.Write(formatRow(reader));
}

由于这是在我们返回行时执行的,因此您不会阻止网络访问,我猜这对您来说是一个巨大的瓶颈。这里的关键是我们不会将任何这些内容长时间存储在内存中,因为我们循环读取器将丢弃结果,并且文件会将行写入磁盘。

于 2009-05-18T16:39:14.037 回答
2

我认为乔希的建议是这样的:

您有循环,您当前在其中遍历查询的所有结果记录(此处仅使用伪代码):

while (rec = getNextRec() )
   {
   put in hash ...
   }

for each rec in (hash)
   {
   format and save back in hash ...
   }

for each rec in (hash)
   {
   write to a file ...
   }

instead, do it like this:

while (rec = getNextRec() )
   {
   format fields ...
   write to the file ...
   }

那么您一次在内存中永远不会有超过 1 条记录......并且您可以处理无限数量的记录。

于 2009-05-18T17:08:21.090 回答
1

显然,一次读取 6000 万条记录会耗尽你所有的内存——所以你不能这样做。(即你的 7 线程模型)。一次读取 6000 万条记录会占用您所有的时间 - 所以您也不能这样做(即您的初始读取文件模型)。

所以....你将不得不妥协并做一些两者兼而有之。

Josh 是对的 - 打开一个光标到您的数据库,它只是以最简单、功能最简单的方式一个接一个地读取下一条记录。“firehose”游标(也称为只读、只进游标)是您想要的,因为它对数据库施加的负载最少。数据库不会让您更新记录,或在记录集中倒退,这是您无论如何都不想要的,因此它不需要处理记录的内存。

现在你有了这个光标,数据库一次给你一条记录——读取它,然后将它写入一个文件(或多个文件),这应该很快完成。然后,您的任务是以正确的顺序将文件合并为 1,这相对容易。

鉴于您必须处理的记录数量,我认为这是您的最佳解决方案。

但是......既然你到目前为止做得很好,为什么不减少线程数量,直到你的内存限制范围内。批处理是许多公司在一夜之间运行的,这似乎只是这些过程中的另一个。

于 2009-05-18T17:39:08.407 回答
0

取决于您使用的数据库,但如果它是 SQL Server,我会建议使用类似 SSIS 的东西来执行此操作,而不是编写程序。

于 2009-05-18T16:36:22.840 回答