4

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

批量大小:0 - sqls 发送:14000 - 持续时间:约 1 分钟

批处理大小:4 - sqls 发送:5000 - 持续时间:超过 10 分钟

批量大小:10 - sqls 发送:2700 - 持续时间:约 5 分钟

批量: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

提前致谢

更新使事情变得清楚:性能损失是在批量获取期间而不是批量更新/插入

4

1 回答 1

5

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

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

所以每个集合都会在这张地图中创建一个条目。如果你有很多收藏的 pojo,就像我的例子一样,它会快速增长。例如,每 32 个集合加载 10.000 个 pojo,您就有 320.000 个集合条目。Hibernate 现在只需遍历映射 (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

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

于 2012-06-04T14:23:31.180 回答