设置setMaxResults时,JPA查询非常缓慢

3
在一个特定的项目中,我遇到了一个在不同配置下使用类型查询的问题。
当在TypedQuery上设置maxResults值大于查询返回的实际结果数时,getResultList()执行起来需要很长时间(有时需要花费长达350秒才能检索到由6列组成的10行)。
相比之下,没有setMaxResults的同一查询需要大约15秒才能执行。如果将WHERE参数更改以检索更多的结果,则同一查询也非常快。
我正在使用资源本地数据源在Junit测试类中进行所有这些测试,但是在Weblogic 10.3.6上使用JTA数据源也会出现相同的情况(以及Jboss EAP 6.1,但我认为这并不重要)。
我尝试在persistence.xml中调整hibernate.jdbc.fetch_size和hibernate.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代码时,我发现ps.executeQuery()上的org.hibernate.jdbc.AbstractBatcher.getResultSet(PreparedStatement)异常缓慢。这是否应该指向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

您可以尝试添加/*+ FIRST_ROWS */提示。如果没有帮助,您将不得不找出Hibernate实际执行的内容。

更新:您的查询需要查看多少数据(以MB为单位)? 15秒的响应时间合理吗? 您能提供该查询的“解释计划”吗?

查看查询,似乎您应该在message0_.CREATION_DATE上建立索引。您可以在where子句中提供一个上限。就像Tom Kytes总是说的那样...好好想想!如果您查询过去某个日期和现在之间的数据,并且没有传递上限(SYSTIMESTAMP)-您基本上期望数据库长时间运行,以了解仍然值得使用日期列上的索引。Oracle会执行“绑定参数预测”之类的操作-它甚至会查看您提供的边界的实际,如果认为成本更低,可能会决定采用不同的查询计划....

你是否意识到 ORDER BY 子句是在 WHERE 子句之进行评估的?这意味着,你将不会按照 ORDER BY 子句获取前 n 行记录,而是获取 n 个随机记录,然后对它们进行排序...

我想尝试避免使用提示,因为它们不受Hibernate支持。此外,我认为您在说这种情况下“ORDER BY”在where子句之后被评估时是错误的,因为我的查询使用了一个子查询(select * from (...) where rownum <= ?)。请参阅经常引用的文章:http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html - Virginie
重要的是将order by放在内部查询中。我想知道,因为你最初的问题没有显示它。 - HAL 9000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接