16

我正在使用 Hibernate 4.1.6 并且在构建列表的速度方面存在问题。我正在运行以下查询。

public void doQuery(final Baz baz){
  final Query query = getSessionFactory().getCurrentSession().createQuery(
          "select c.id, foo.someValue from Foo as foo "+
          "join foo.a as a"+
          "join foo.b as b "+
          "join b.c as c "+
          "where baz=:baz"
          );
  query.setParameter("baz", baz);
  Long start=System.currentTimeMillis();
  final List<Object[]> list = query.list();
  Long end=System.currentTimeMillis();
  System.out.println((end-start));
}

我设置了休眠调试以获取发送到数据库的实际查询。我直接在数据库中运行了该查询,它在 0.015 毫秒内返回了 23,000 行。所以,我猜查询不是问题。上面的示例显示创建该列表大约需要 32 秒。有什么办法可以加快速度吗?

更新:我尝试使用使用休眠调试查询的 createSQLQuery() 方法,它的运行速度与 createQuery() 方法一样慢。

更新:我尝试使用无状态会话,但运行速度同样慢。

更新:我输出了一些统计数据(将 hibernate.generate_statistics 标志设置为 true),但对我来说没有什么令人担忧的:

Hibernate SessionFactory Statistics [
    Number of connection requests[4]
    Number of flushes done on the session (either by client code or by hibernate[3]
    The number of completed transactions (failed and successful).[3]
    The number of transactions completed without failure[3]
    The number of sessions your code has opened.[4]
    The number of sessions your code has closed.[3]
    Total number of queries executed.[4]
    Time of the slowest query executed.[28258]
    the number of collections fetched from the DB.[6]
    The number of collections loaded from the DB.[6]
    The number of collections that were rebuilt[0]
    The number of collections that were 'deleted' batch.[0]
    The number of collections that were updated batch.[0]
    The number of your objects deleted.[0]
    The number of your objects fetched.[1]
    The number of your objects actually loaded (fully populated).[204]
    The number of your objects inserted.[1]
    The number of your object updated.[0]
]

Hibernate SessionFactory Query Statistics [
    total hits on cache by this query[0]
    total misses on cache by this query[0]
    total number of objects put into cache by this query execution[0]
    Number of times this query has been invoked[1]
    average time for invoking this query.[28258]
    maximum time incurred by query execution[28258]
    minimum time incurred by query execution[28258]
    Number of rows returned over all invocations of this query[23303]
]

更新:从本机查询的 ScrollableResults 执行 next() 时,我看到同样的缓慢。请注意,我在循环中什么也没做。

    ScrollableResults results = query.scroll();
    Long start=System.currentTimeMillis();
    while (results.next()) {
       //do nothing
    }
    Long end=System.currentTimeMillis();
    System.out.println((end-start));
4

5 回答 5

8

我不能 100% 确定这个答案,因为调优/优化问题总是很难确定。

但是,基于您打开show_sql、提取查询并直接针对数据库运行它并通过 Hibernate 查询查看亚秒级结果与执行时间的事实,我将重点关注 Hibernate 构建和水合对象的方式通话的结果query.list()

这是另一个用户,他提到了 Hibernate 中类似的查询性能问题,并通过在 POJO 中添加完整的便利构造函数(接受每个字段的值的构造函数)看到了性能的显着提升:Simple hibernate query return very slow

听起来他们偶然发现了这个修复程序,并且不清楚为什么会这样。有人猜测 Hibernate 必须使用反射来检测属性。我自己很好奇,并计划在有机会时深入研究 Hibernate 的源代码,以便更好地理解这一点。不过,与此同时,您可能希望考虑为所有 POJO 类属性添加这些带有参数的完整构造函数,看看这是否会产生影响。

请让我知道你发现了什么,因为我对 Hibernate 性能优化非常感兴趣。谢谢!

于 2012-08-28T17:05:30.147 回答
4

如果查询(带有show_sql)似乎没有问题,那么它可能在代码中。启动 VisualVM(随 JDK 一起提供jvisualvm)并使用其 CPU 分析器找出哪些方法花费的时间最长。

于 2012-08-28T19:11:17.437 回答
1

我直接在数据库中运行了该查询,它在 0.015 毫秒内返回了 23,000 行。所以,我猜查询不是问题。

这可能为时过早,因为查询执行时间不仅仅取决于查询文本。即使他们的查询是在相同的数据上运行的,你怎么知道数据库使用了相同的执行计划?你怎么知道它在磁盘缓存中获得了相同数量的缓存命中?例如,hibernate 在与数据库通信时使用准备好的语句,但您可能没有。在 Oracle 中,执行计划由查询文本缓存,因此不同的查询文本意味着新计算的执行计划。由于缓存的执行计划可能是基于不同的查询参数形成的,因此它很可能是不同的——并且可以将执行时间改变几个数量级。请注意,我并不是说它数据库,但我不会低估这种可能性。

因此,您应该做的第一件事是衡量数据库或在您的 JVM 中运行的东西是否在浪费所有时间。一种简单的方法是在执行查询时观察 JVM 的 cpu 消耗。如果它明显少于一个线程,则 JVM 正在等待某些东西——可能是数据库。

如果是数据库,请使用数据库的优化工具来捕获执行计划,以及其他相关的性能指标。

如果它在 JVM 中,请使用 Profiler 来查明性能瓶颈。

于 2012-08-29T18:38:04.280 回答
0

我们遇到了类似的问题,不知道是否相关。基本上,由于我们每次查询都会更新一次新的 SessionFactory,它会执行如下查询:

 select streamref0_.UUID as UUID145_, streamref0_.Tape_TapeId as Tape2_145_ from StreamRefToTape streamref0_ where streamref0_.UUID=?

你会注意到那里有大量的数字。事实证明,每个新的会话工厂都会增加一次。无论如何,这导致 oracle 花费所有时间为每个查询制定一个新计划(它报告说 cpu 几乎都在“硬解析”时间生成新计划——我猜 Oracle 生成它没有看到的计划的速度很慢前?)。在这种特殊情况下,解决方法是每次只使用同一个工厂而不是新工厂。另请参阅Hibernate 为每个查询生成不同的 SQL

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2588723819082解释了硬解析,这显然很糟糕。

另一个可能的解决方法是在休眠中使用“原始 sql”(jdbc)或可能的原始 sql 查询,尽管在这种特殊情况下似乎并没有解决问题......

于 2013-03-06T20:28:00.367 回答
0

我不确定,但我在当前项目中面临这个问题。

在我的情况下,问题是hibernate用于cross join执行隐式连接,因此(在我看来)从数据库中获取数据以构造结果(可能使用反射)后需要时间。

我的解决方案是明确使用内部联接。

对于你的问题,我认为你可以inner join明确地使用而不是仅仅使用join.

于 2020-06-21T14:51:14.830 回答