来自Hibernate App的查询不使用DB索引

时间:2012-01-04 15:55:12

标签: performance oracle hibernate indexing

我尝试解决应用程序的性能问题。查询hibernate生成的形式为:

select * 
from ( 
    select this.a, this.b, this.state, this.id 
    from view_user this 
    where this.state=:1 order by this.a asc, this.b
) 
where rownum <= :2

,其中

  • id是主键
  • (a,b,id)上有一个组合的唯一索引。
  • view_user有大约200万条目
  • view_user执行其他表的其他连接

问题

以上查询执行   - 快速从SQLDeveloper   - 快速从一个带有hibernate的小型Java应用程序   - 使用hibernate的应用程序非常慢(大约100倍)   - 绑定变量的值分别为2(来自分页的rownum起源)   - hibernate查询是上面的“形式”。视图中实际上有大约20列。

目前的分析状况

  • 查询计划显示当查询来自SQlDeveloper或“小型Java应用程序”时使用索引。
  • 查询计划显示如果查询来自hibernate app
  • ,则执行全表扫描
  • 数据库跟踪只显示两个不同之处:NLS设置(来自SQLDeveloper)和略有不同的格式(空格)。其他一切似乎都是一样的......

版本

  • hibernate:2.1.8
  • jdbc驱动程序:使用ojdbc14,5和6.没有区别
  • Oracle:10.2和11.没有区别

=&GT;关于这个问题,我很高兴有人可能会提出一些暗示。让我感到困扰的是,DB跟踪没有显示出任何差异...是的,它看起来像是关于休眠的东西。但是什么?怎么检测?


为了完整起见,这里是hibernate查询(来自日志):

Select * from ( 
    select this.USER_ID as USER_ID0_, this.CLIENT_ID as CLIENT_ID0_, 
    this.USER_NAME as USER_NAME0_, this.USER_FIRST_NAME as USER_FIR5_0_, this.USER_REMARKS as 
    USER_REM6_0_, this.USER_LOGIN_ID as USER_LOG7_0_, this.USER_TITLE as USER_TITLE0_, 
    this.user_language_code as user_lan9_0_, this.USER_SEX as USER_SEX0_, 
    this.USER_BIRTH_DATE as USER_BI11_0_, this.USER_TELEPHONE as USER_TE12_0_, 
    this.USER_TELEFAX as USER_TE13_0_, this.USER_MOBILE as USER_MO14_0_, 
    this.USER_EMAIL as USER_EMAIL0_, this.USER_ADDRESSLINE1 as USER_AD16_0_, 
    this.USER_ADDRESSLINE2 as USER_AD17_0_, this.USER_POSTALCODE as USER_PO18_0_, 
    this.USER_CITY as USER_CITY0_, this.USER_COUNTRY_CD as USER_CO20_0_, 
    this.USER_COUNTRY_NAME as USER_CO21_0_, this.USER_STATE_ID as USER_ST24_0_, 
    this.USER_STATE as USER_STATE0_, this.USER_TEMP_COLL_ID as USER_TE26_0_, 
    this.USER_TEMP_COLL_NAME as USER_TE27_0_, this.UNIT_ID as UNIT_ID0_, 
    this.CLIENT_NAME as CLIENT_38_0_, this.PROFILE_EXTID as PROFILE39_0_
    from VIEW_USER this
    where this.USER_STATE_ID=:1 order by this.USER_NAME asc, this.USER_FIRST_NAME asc
) 
where rownum <= :2

唯一索引超过user_name,user_first_name,user_id。

5 个答案:

答案 0 :(得分:4)

我遇到过类似的情况,所以这可能与你有关。

JDBC驱动程序正在将您的参数更改为unicode,以便varchar在到达数据库时变为nvarchar。如果幸运的话,SQL 2008 SP1会捕获并将其转换回来。但SQL 2000和SQL 2005不会,并且查询优化器将执行全表扫描而忽略您的索引。

您可以通过在连接字符串中添加连接参数sendStringParametersAsUnicode=FALSE来在JDBC层修复此问题。

答案 1 :(得分:1)

您提供的SQL看起来不像Hibernate生成的查询。你确定这不是一个手写的查询吗?

如果要使用Hibernate,可以使用setMaxResults()来限制返回的行数。

如果你想使用手写的查询,我想你想要这样的东西:

select * 

来自(     选择this.a,this.b,this.state,this.id     来自view_user这个     其中this.state =:1 by this.a asc,this.b )其中rownum&lt; =:2

答案 2 :(得分:1)

问题的最终解决方案是c3p0。我们无法找出选择错误查询计划的原因。我们最后的假设是它与dbcp的连接初始化有关。但由于时间的关系,我们尝试了c3p0。第一次经历:

  • 问题未出现
  • 更快,因为在使用为池配置的maxConnections方面更具攻击性
  • 由于上述要点,我们的完整应用程序现在更快。这是基于使用应用程序时的总体印象以及我们的各种负载测试

因此,目前我们正急切地使用c3p0测试各种方案,以便为生产系统做好准备。

感谢所有投入!

答案 3 :(得分:0)

经过大量的调试和尝试后,我得到了如何解决问题的答案。但是,我无法解释究竟是什么问题。

问题似乎是在JDBC连接池级别上。我的配置是:

  • INITIALSIZE = 0
  • minIdle = 10

使用上述配置,我们遇到问题descritpion中描述的症状。到目前为止,解决方案似乎是将minIdle设置为initialSize。否则,连接似乎以某种方式初始化不同,我们看到性能下降。所以,我们将两者都设置为'10'。

我目前正在分析日志。在数据库服务器上,跟踪文件不显示任何差异。到目前为止,JDBC跟踪并未显示任何有趣的信息。

谷歌搜索,我看到很多样本配置设置为minIdle = initialSize。此外,我发现了两个关于JDBC设置的引用(spring docu,vmware docu):

  

minIdle:应始终保留在池中的最小已建立连接数。如果验证查询失败,连接池可以缩小到此数字以下。默认值来自initialSize。

请注意,在minIdle的所有文档中都找不到最后一句“派生自initialSize”。大多数文件都提到“0”为默认值。

答案 4 :(得分:0)

对于具有SQL Server设置的nvarchars,上面/下面有一个答案 - 这里是Oracle的类似设置......

检查以确保某人未设置属性oracle.jdbc.defaultNChar = true

这有时会解决unicode问题,但这意味着所有列都被视为nvarchars。如果varchar列上有索引,则不会使用它,因为oracle必须使用函数来转换字符编码。