通常的休眠性能陷阱

时间:2010-05-04 09:10:08

标签: java performance hibernate orm

我们刚刚完成了我们的申请。 (她开始变慢)。 问题似乎是“在休眠状态”。

这是传统的映射。谁工作,做它的工作。关系背后的关系也很好。

但是有些请求很慢。

因此,我们很感激任何有关hibernate常见和常见错误的输入,这些错误最终会导致响应缓慢。

例如:渴望代替懒惰可以大大改变响应时间......


编辑:像往常一样,阅读手册通常是个好主意。整整一章涵盖了这个主题:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html

7 个答案:

答案 0 :(得分:17)

最常见的陷阱之一是臭名昭着的n+1 selects problem。默认情况下,Hibernate不会加载您不需要的数据。这样可以减少内存消耗,但会让您暴露于n + 1选择问题,这可以通过切换到正确的获取策略来检索在适当初始化状态下加载对象所需的所有数据来避免。

但是也不要取得太多,否则你会遇到相反的问题,笛卡尔产品问题:你可能最终会创建检索的语句而不是执行许多SQL语句很多数据。

这就是调整的重点:找到应用程序的每个用例(或者至少是需要调整的用例)的数据不够和过多的中间位置。

我的建议:

  • 首先在Hibernate级别激活SQL日志记录
  • 运行关键用例(20%使用80%的时间)或者甚至所有这些用例如果你有这种奢侈
  • 识别可疑查询并优化获取计划,检查是否正确使用了索引等
  • 涉及您的DBA

答案 1 :(得分:2)

我想赞同Pascal所说的一切,并且另外提到“检索太多数据问题”的一个解决方案是使用Hibernate Projections,并将数据提取到平面DTO中,该DTO只包括列确实需要。当您不打算更新此Hibernate会话中的数据时,这是一个特别的选项,因此不需要映射对象。取自this article,其中包含更多Hibernate性能提示。

答案 2 :(得分:2)

最常见的错误是隐藏在幕后的N + 1选项。通常在生产之前没有检测到这些,并且通常的剖析器不能很好地揭示它们。

您可以尝试像XRebel这样的交互式探查器 - 它会一直运行并在开发过程中显示问题,因此您可以立即修复它们并培训您的开发人员不要犯这些错误。

http://zeroturnaround.com/blog/hibernate-orm-with-xrebel-revealing-multi-query-issues-with-an-interactive-profiler/

XRebel showing SQL queries

答案 3 :(得分:1)

我脑海中想到了一件事。您可以看到加载实体是否还加载了一些serialized实体,每次加载实体时都会对其进行反序列化。此外,在提交事务休眠时,可能会为您执行flush()(可配置)。如果它刷新,为了保持持久性,它将对提交的权限和数据库中的权限进行比较。在这种情况下,它将对serialized对象进行比较,这需要很长时间。

你可以做的另一件事是检查你是否有任何不必要的持久性级联,即列@Cascade({CascadeType.PERSIST, CascadeType.SAVE_UPDATE})注释。

你可以做的另一件事是与hibernate特别相关的是你创建view来做一个单一的查询,而不是做很多很多的查询到不同的表。这对我们在某个特征上产生了巨大的影响。

答案 4 :(得分:1)

正如其他人已经提到的,N+1 problem是JPA应用程序中的常见问题。我目前正在开发一种工具,用于在单元测试和测试环境中及早发现这些问题。它被称为JDBC Sniffer,它是开源的,完全免费的

它允许您直接在浏览器中监控测试环境中执行的查询数量: enter image description here

它还为流行的单元测试框架提供了extensino,以便在您仍使用注释开发代码时捕获N + 1问题:

import com.github.bedrin.jdbc.sniffer.BaseTest;
import com.github.bedrin.jdbc.sniffer.Threads;
import com.github.bedrin.jdbc.sniffer.Expectation;
import com.github.bedrin.jdbc.sniffer.Expectations;
import com.github.bedrin.jdbc.sniffer.junit.QueryCounter;
import org.junit.Rule;
import org.junit.Test;

public class QueryCounterTest extends BaseTest {

    // Integrate JDBC Sniffer to your test using @Rule annotation and a QueryCounter field
    @Rule
    public final QueryCounter queryCounter = new QueryCounter();

    @Test
    @Expectations({
            @Expectation(atMost = 1, threads = Threads.CURRENT),
            @Expectation(atMost = 1, threads = Threads.OTHERS),
    })
    public void testExpectations() {
        executeStatement();
        executeStatementInOtherThread();
    }

}

答案 5 :(得分:0)

正如大家已经提到的,Hibernate的性能完全取决于正确的设置。通过切换到无状态会话,我曾经能够将一些凭证缓存刷新速度提高10倍(从2s到200ms)(特定代码不依赖于任何类型的延迟提取,所以我可以安全地做我做的事情)

答案 6 :(得分:0)

m:n和n:1关系的映射是Hibernate频繁性能问题的根源。

如果您使用HQL,Hibernate缓存无济于事,因为Hibernate无法将查询转换为缓存调用,因此无法使用HQL缓存(至少在我阅读代码时)。

如果您想要简单,快速和轻量级的方法,我可以推荐fjorm

Dislaimer:我是fjorm项目的创始人