通常的hibernate性能陷阱

我们刚刚完成了我们的应用程序。 (她开始变慢)。 问题似乎是“在hibernate状态”。

这是一个遗留映射。 谁工作,做它的工作。 关系背后的关系也是好的。

但是有些要求很慢。

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

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


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

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

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

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

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

我的建议:

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

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

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

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

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

XRebel显示SQL查询

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

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

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

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

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

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

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

免责声明:我是fjorm项目的创始人

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

它允许您直接在浏览器中监控在测试环境中执行的查询数量: 在此处输入图像描述

此外,它还为流行的unit testing框架提供了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(); } }