在会话中加载实体后,Hibernate查询会大幅减慢

时间:2009-11-24 16:03:59

标签: hibernate hql

我正在使用Hibernate EntityManager,并且在我的Hibernate查询中遇到了奇怪的减速。看看这段代码:

public void testQuerySpeed() {
    for(int i = 0; i < 1000; i++) {
        em.createNativeQuery("SELECT 1").getResultList();
    }
}

这在我的机器上运行大约750毫秒。考虑到它只是选择一个恒定的整数,但不是很快,但可以接受。在我启动查询之前,我的问题出现在我的EntityManager会话中加载了任何实体:

public void testQuerySpeed() {
    CommercialContact contact = em.find(CommercialContact.class, 1890871l);

    for(int i = 0; i < 1000; i++) {
        em.createNativeQuery("SELECT 1").getSingleResult();
    }
}

em.find()很快,但运行时1000查询增加了十倍以上,大约10秒。如果我在em.clear()之后放置em.find(),则问题会再次消失,运行时间会回到750毫秒。

我在这里使用了原生查询,但HQL查询也存在问题。每当实体进入EntityManager会话时,似乎所有查询都至少需要70ms。

在生成需要n + 1个查询的列表时,这种性能下降确实对我们造成了伤害。

我已经测试了最新的Hibernate 3.5 beta,并且遇到了完全相同的问题。有没有人见过这个问题,或者有关如何解决这个问题的想法?

我正在使用PostgreSQL 8.3,使用资源本地事务(在Tomcat中运行)。使用内置连接池,但使用C3P0没有任何区别。

3 个答案:

答案 0 :(得分:13)

我还必须建议使用JVM分析器来查看时间的去向。打开Hibernate会话的SQL语句记录也可能没有什么坏处,只是为了确保你没有运行比你想象的更多的SQL。

这里首先想到的是Hibernate Session的“刷新”行为。您是否在Session上明确设置了特定的刷新模式?如果没有,那么你会得到“自动”刷新,这会对会话中的对象进行一些检查,以确定是否存在需要“刷新”回数据库的内存更改(在事务内部) ,当然)。

我想首先尝试查看它是否有任何影响最简单的方法是修改上面显示的测试代码,以指定在提交数据库事务时手动进行刷新:

public void testQuerySpeed() {
    em.setFlushMode(FlushModeType.COMMIT); // assuming you're using JPA annotations
    CommercialContact contact = em.find(CommercialContact.class, 1890871l);

    for(int i = 0; i < 1000; i++) {
        em.createNativeQuery("SELECT 1").getSingleResult();
    }
}

我想的另一个想法是询问您是否可以在单独的EntityManager中执行批量任务,如果您只是执行UPDATE或INSERT,这可能会有效。

答案 1 :(得分:4)

我基本上有同样的问题(在循环内查询)。我用JProfiler进行了一次分析验证......我感兴趣的方法的执行花了572秒,而休眠脏检查花了457秒这个时间(大约80%)。太棒了,不是吗?我必须说我有很多EntityManager管理的实体。如果我在违规代码中引入em.flush()/ em.clear()或em.setFlushMode(FlushModeType.COMMIT),性能问题就会消失。

个人资料结果可在http://img1.imagilive.com/0110/hibernate_dirty_checking_bad_perfomances0ce.png

获取

答案 2 :(得分:1)

你执行的本机查询没有说明它将触及什么,因此Hibernate必须(为了保持一致)flush()来反对它所知道的所有表中的所有数据(并且你的单个find()可能已经获取了不止一个对象,因此它可能不是一个简单的操作。)

要优化此操作,请确保使用SQLQuery.add *方法来定义查询实际执行的操作。在这种情况下,query.addSynchronizedQuerySpace(“bogustablename”)应该告诉Hibernate这个查询只是来自没有特定表的标量数据。