仅当查询包含FIRST_ROWS或ROWNUM限制时,ResultSet.next才会非常慢

时间:2012-01-14 09:30:56

标签: java oracle jdbc eclipselink

我使用

执行本机查询
entityManager.createNativeQuery(sqlQuery);
query.setMaxResults(maxResults);

List<Object[]> resultList = query.getResultList();

为了加快查询速度,我想包含FIRST_ROWS(n)提示或使用WHERE ROWNUM > n进行限制。

使用检测,我发现确实OraclePreparedStatement.executeQuery确实更快,但在EJBQueryImpl.getResultList花费了更多时间导致整体性能非常差。仔细研究一下,我发现ResultSet.next()的每10次调用与executeQuery本身()相同。当我省略查询提示或ROWNUM条件时,这种奇怪的行为就会停止,然后每10次调用resultset.next会比其他调用稍微低一些,但只有2ms而不是3秒。

3 个答案:

答案 0 :(得分:2)

包含提示时,您是否获得了不同的查询计划?我的假设是你根据你对问题的描述做了。

当您在Oracle中执行查询时,数据库通常不会在任何时间点实现整个结果集(显然,如果您指定需要所有数据的ORDER BY子句,则可能必须这样做在排序发生之前实现)。在客户端开始获取数据之前,Oracle实际上并未开始实现数据。它运行足够的查询以生成客户端要求获取的许多行(在您的情况下听起来像是10),将这些结果返回给客户端,并在继续处理之前等待客户端请求更多数据查询。

听起来好像包含FIRST_ROWS提示时,查询计划的更改方式使得执行起来更加昂贵。显然,这不是FIRST_ROWS提示的目标。目标是告诉优化器生成一个计划,即使从查询中获取所有行效率较低,也可以更有效地获取前N行。这往往会导致优化器支持像表扫描的索引扫描之类的事情,其中​​表扫描整体上可能更有效。听起来好像在你的情况下,优化器的估计是不正确的,它最终会选择一个通常效率较低的计划。这通常意味着您的查询引用的某些对象的某些统计信息不完整或不正确。

答案 1 :(得分:2)

听起来你使JDBC executeQuery更快,但JDBC ResultSet接下来更慢。您更快地执行查询但是获取数据的速度更慢。似乎是一个JDBC问题,而不是EclipseLink,如果您实际获取了数据,则可以通过原始JDBC获得相同的结果。

10是默认的提取大小,因此您可以尝试将其设置为更大。

请参阅, http://www.eclipse.org/eclipselink/api/2.3/org/eclipse/persistence/config/QueryHints.html#JDBC_FETCH_SIZE

答案 2 :(得分:0)

尝试直接向SQL添加最大行限制,而不是使用setMaxResults,即添加rownum&lt; maxResults到sql字符串。 EclipseLink在创建SQL时会在查询中使用rownum来获取最大行数,但由于您使用自己的SQL,因此它将使用结果集来限制行。