设置setMaxResults时,JPA查询速度极慢

时间:2014-08-04 11:11:25

标签: java sql oracle hibernate jpa

在某个特定项目中,我遇到了不同配置的类型化查询问题。

在TypedQuery上设置maxResults值大于查询返回的实际结果数时,getResultList()需要很长时间才能执行(有时最多需要350秒来检索由6列组成的10行)。 / p>

相反,没有setMaxResults的相同查询大约需要15秒才能执行。更改WHERE参数的相同查询以检索更多数量的结果也非常快。

我正在使用资源本地数据源在Junit测试类中测试所有这些,但Weblogic 10.3.6上的JTA数据源(以及Jboss EAP 6.1)也是如此,但我不认为它是有关)。

我尝试在persistence.xml中使用hibernate.jdbc.fetch_sizehibernate.jdbc.wrap_result_sets,但很难估计是否有改进:一旦查询成功并因此被Oracle缓存,它将会无论我如何调整这些值,我都可以快速执行(我没有权限清除Oracle的语句缓存)。

我注意到如果从查询中删除ORDER BY,执行时间会下降到50-60秒,这仍然太长但仍然不到350秒。虽然这列是索引的(它是主键),但我不明白为什么它会对性能产生太大的影响。

我正在使用Criteria API构建我的查询,但我也尝试使用HQL或本机SQL。我正在使用CriteriaQuery#multiSelect仅检索所需的字段,然后将结果分配给具有适当构造函数(select new ...)的类。

这是由Hibernate翻译的查询:

SELECT * FROM
  (SELECT message0_.MESG_ID  AS col_0_0_,
    message0_.CREATION_DATE  AS col_1_0_,
    messagetyp1_.MSGT_ID     AS col_2_0_,
    message0_.MESSAGE_NAME   AS col_3_0_,
    interview2_.INVW_ID      AS col_4_0_,
    interview2_.IVWT_ID      AS col_5_0_,
    party3_.LICENSE_USERNAME AS col_6_0_
  FROM  message0_
  LEFT OUTER JOIN S399_MESSAGE_TYPES messagetyp1_
  ON message0_.MSGT_ID=messagetyp1_.MSGT_ID
  LEFT OUTER JOIN S399_INTERVIEWS interview2_
  ON message0_.INVW_ID=interview2_.INVW_ID
  LEFT OUTER JOIN S399_PARTIES party3_
  ON message0_.PRTY_ID          =party3_.PRTY_ID
  WHERE message0_.CREATION_DATE>=?
  AND (interview2_.IS_POLLING  IS NULL
  OR interview2_.IS_POLLING     =0)
  ORDER BY message0_.MESG_ID DESC
  )
WHERE rownum <= ?

当我在SQLDeveloper中运行它时,速度很快(应该是这样),一般不到一秒钟。

当我调试Hibernate的代码时,我可以看到org.hibernate.jdbc.AbstractBatcher.getResultSet(PreparedStatement)上的ps.executeQuery()异常缓慢。它应该指向JDBC驱动程序问题还是指向数据库问题?

提前感谢您对此情况的任何建议或想法。

配置:

  • Java 6
  • Hibernate 3.6.10(JPA 2.0)(尝试升级到Hibernate 4和JPA 2.1,没有变化)
  • Oracle数据库11g(11.1.0.7.0)
  • Oracle jdbc驱动程序11.2.0.3(尝试使用11.1.0.7.0和11.2.0.4,无更改)。
  • hibernate.dialect:Oracle10gDialect。

可能重复:ResultSet.next very slow only when query contains FIRST_ROWS or ROWNUM restriction

1 个答案:

答案 0 :(得分:0)

您可以尝试添加/*+ FIRST_ROWS */提示。如果它没有帮助你必须找出Hibernate实际执行的内容。

更新:您的查询需要查看多少数据(以MB为单位)? 15秒的响应时间是否合理?你能提供一个&#34;解释计划&#34;该查询?

查看查询,您似乎应该在message0_.CREATION_DATE上有一个索引。您可以尝试在where子句中提供上限。就像Tom Kytes总是说......想想吧!如果您在过去和现在的某个日期之间查询数据,并且您没有传入上限(SYSTIMESTAMP) - 您基本上期望数据库能够很长时间地知道它仍然值得信赖在日期列上使用该索引。 Oracle做的事情就像&#34;绑定参数偷看&#34; - 它甚至会查看您提供的边界的实际,如果它认为成本更低,它可以决定使用不同的查询计划....

您是否意识到在 where子句之后评估ORDER BY?这意味着,根据您的订单,您不会获得前n个记录,但随后会记录n个随机记录....