场景的最佳设计

时间:2009-05-18 16:32:43

标签: java database performance hashmap

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

  • 目前我正在从DB中选择记录(7天选择7天)并将它们放入HashMap中。从HashMap读取并形成一些列,最后写入文件(单独的文件7天)。
  • 最后,我将所有7个文件合并到一个文件中。

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

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

请为这个场景建议最好的设计,我应该使用一些缓存机制,如果是,那么哪一个以及如何?

注意:客户端不希望在数据库中更改任何内容,如创建索引或存储过程,他们不想触摸数据库。 提前谢谢。

4 个答案:

答案 0 :(得分:4)

您是否需要在内存中记录所有记录以格式化它们?您可以尝试通过进程和文件直接传输记录。如果你甚至可以进一步打破查询,你可以开始处理结果,而你仍然可以检索它们。

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

修改

我是.net开发人员,所以让我建议我在.net中做什么,希望你可以转换成java方面的类似技术。

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

IDataReader reader=GetTheDataReader(dayOfWeek);

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

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

答案 1 :(得分:2)

我认为Josh的建议是:

你有循环,你当前浏览查询的所有结果记录(这里只使用伪代码):

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条记录......而且你可以处理无限数量的记录。

答案 2 :(得分:1)

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

所以....你将不得不妥协并做两件事。

Josh没错 - 向你的数据库打开一个光标,只需以最简单,功能最强的方式一个接一个地读取下一条记录。您需要一个“firehose”游标(也称为只读,仅向前游标),因为它对数据库施加的负载最小。数据库不会让你更新记录,也不会在记录集中向后移动,这是你不想要的,所以它不需要处理记录的内存。

现在你有了这个光标,数据库一次给你一条记录 - 读它,然后把它写到一个文件(或几个文件),这应该很快完成。那么你的任务就是用正确的顺序将文件合并为1,这相对容易。

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

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

答案 3 :(得分:0)

取决于您使用的数据库,但如果它是SQL Server,我建议使用像SSIS这样的东西而不是编写程序。