使用@batchsize后jpa性能崩溃

时间:2012-05-31 13:25:49

标签: java performance hibernate jpa

我有一个java jpa / hibernate应用程序需要获取大量数据才能执行其任务。我遇到了n + 1问题,所以我决定使用hibernate.default_batch_fetch_size(@batchsize)属性来降低所需的sql往返。我尝试了一些值,但是几乎所有尝试过的值都崩溃了。

batchsize:0 - sqls发送:14000 - 持续时间:约1分钟

batchsize:4 - sqls发送:5000 - 持续时间:超过10分钟

batchsize:10 - sqls发送:2700 - 持续时间:约5分钟

batchsize:100 - sqls发送:400 - 持续时间:约1分钟

这是一种“正常”行为吗?如果不是什么可能是错误?

我用log4jdbc记录了生成的sql。我注意到每个批次声明之间的差距大约为100-150毫秒。如果我稍后运行sql,则每个语句的运行时间不超过20毫秒。所以这不是一个与DB(IN语句)相关的问题。

Java:1.6.0_31,Hibernate 3.6.7,DB Postgres 9.1.1,JDBC postgresql-9.1-901.jdbc4.jar

提前致谢

更新 要明确:性能损失是在批量提取期间而不是批量更新/插入

1 个答案:

答案 0 :(得分:5)

经过一些调整后我发现了这个问题。 Hibernate(至少在版本3.6.7中)将所有映射的集合存储在映射中。您可以使用这样的snipplet访问这些地图:

SessionImpl si = ((SessionImpl) entityManager.getDelegate());
PersistenceContext persistenceContext = si.getPersistenceContext();
persistenceContext.getCollectionEntries();

所以每个集合都会在这个地图中创建一个条目。如果你有很多集合的pojos,就像我的情况一样,它会快速增长。例如,每个32个集合加载了10.000个pojos,你有320.000个集合条目。 Hibernate现在只是迭代遍历map(org.hibernate.engine.BatchFetchQueue.getCollectionBatch(CollectionPersister,Serializable,int,EntityMode))来查找未加载的Collection id以便稍后将它们放在IN子句中。 Hibernate不会将密钥的搜索限制在特定类型的集合中,因此情况会更糟。

我想我必须清理一些集合,并希望hibernate能够更有效地找到更高版本的密钥。

更新: 这个关于hibernate jira的评论对于有同样问题的人来说可能很有趣: https://hibernate.onjira.com/browse/HHH-1775?focusedCommentId=42686&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-42686

更新: 这个问题在hibernate版本中得到了解决:4.1.8