批量导入大型数据集时的数据库性能建议

时间:2008-12-23 00:30:32

标签: database performance hibernate caching jpa

我正在使用Java和Hibernate的JPA实现构建数据库Web应用程序。应用程序跟踪对象。它还必须批量导入旧版源中的对象。

例如,假设我们正在跟踪人员。该数据库具有名为Person和Address的表。有相应的JPA实体和DAO类。

在JPA层之上是一个负责各种操作的服务层。一种操作是从外部遗留源(例如电话簿中的人)导入可能大量的数据集。对于每个人,它必须检查它是否已存在于数据库中。然后必须根据需要创建或更新此人。每个人都有一个地址,因此必须进行适当的交叉引用和地址创建。

我的问题是,对于大型数据集,此操作可能会很慢。我目前的算法是:

for (Person person: allPersons)
{
    check if person exists in database
    check if address exists in database
    create or update person and address as necessary
}

您建议如何提高绩效?

我可以想到:

  1. 更改导入逻辑以使用查询检索数据并将数据存储到数据库。例如,不是检查for循环中是否存在person,而是在一个查询中将所有人员密钥提交到数据库,该过程中每个检索到的人都在内存中。
  2. 在DAO类中添加我自己的缓存。
  3. 使用外部缓存解决方案(例如memcached)。
  4. 我总是可以通过重组来使用#1来最小化查询。缺点是我的服务层现在非常了解DAO层。它的实现现在由较低的数据库层决定。还有其他问题,如使用太多内存。这种从内存中获取的数据库然后进程似乎非常本土化,并且违背JPA等现成的解决方案。我很好奇其他人在这种情况下会做些什么。

    编辑:缓存无效,因为在循环中查询的每个人都不同。

2 个答案:

答案 0 :(得分:1)

我找到了两种解决方案。一种是一次处理一个块。每个块关闭后重新启动会话。我试图在会话中使用flush clear方法,但有时它只是像你期望的那样运行。在批次之间启动和停止交易似乎效果最好。

如果性能是一个主要问题,那么您只需要在JDBC中进行分解。 Hibernate为大型数据集的批处理增加了太多开销,其中内存和性能很重要。

答案 1 :(得分:0)

您的方法会导致针对数据库的单个查询过多;看起来像4n + 1.如果可能的话,我会写一个查询(可能是在原始SQL中),一次性检查人员+地址的存在。

您可能希望使用StatelessSession而不是标准的Hibernate会话。由于它没有第一级缓存,因此应该降低内存需求。

http://www.hibernate.org/hib_docs/reference/en/html/batch-statelesssession.html

如果这对您不起作用,那么您将需要查看Hibernate中的批处理选项:

http://www.hibernate.org/hib_docs/reference/en/html/batch.html