【问题标题】:Query from Hibernate App does not use DB indexes来自 Hibernate App 的查询不使用数据库索引
【发布时间】:2012-02-02 12:36:24
【问题描述】:

我尝试解决我的应用程序的性能问题。 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 的快速 - 从带有休眠功能的小型 Java 应用程序中快速运行 - 使用休眠的应用程序非常慢(> 100x 慢) - 绑定变量的值分别为 2 和 30(rownum 来自分页) - 休眠查询是上面的“形式”。视图中实际上大约有 20 列。

当前分析状态

  • 查询计划显示当查询来自 SQlDeveloper 或“小型 Java 应用程序”时使用索引。
  • 查询计划显示,如果查询来自休眠应用程序,则会执行全表扫描
  • DB 跟踪仅显示两个差异:NLS 设置(来自 SQLDeveloper)和稍有不同的格式(空格)。其他一切似乎都一样...

版本

  • 休眠:2.1.8
  • jdbc 驱动:使用 ojdbc14、5 和 6。没有区别
  • Oracle:10.2 和 11。没有区别

=> 我很高兴有人可能对这个问题提出任何提示。令我困扰的是数据库跟踪没有显示任何差异的事实......是的,看起来它与休眠有关。但是什么?如何检测?


为了完整起见,这里是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。

【问题讨论】:

  • 我怀疑您在此处输入的查询中有错字,因为内部选择中有两个 wherewhere rownum 子句实际上是在外部选择上吗?
  • 您说“view_user 有 ~ 2 个 mio 条目”。请定义“mio”。另外,view_user.state 的可能值是多少,每个可能值有多少行?最后,为 :1 和 :2 提供了哪些值?谢谢。
  • sry,mio 是百万。内部选择的完整返回集可能是完整视图 (view_user) 的 80% 左右,因为 "this.state=:1" 的值为 2 表示 :1。
  • :2 是 30。我更新了最初的问题描述。
  • 是的,有。 state_id 是表的外键,其中 state_id 是主键

标签: performance oracle hibernate indexing


【解决方案1】:

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

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

您可以在 JDBC 层通过将连接参数 sendStringParametersAsUnicode=FALSE 添加到连接字符串来解决此问题。

【讨论】:

  • 谢谢,这是一种享受。我正在使用休眠 4.2.16 和 SQL Server 2008 SP4 和 sqlJdbc4 Microsoft 驱动程序。遇到同样的问题,我在 sql 客户端上执行完全相同的查询,它会立即返回,通过 java + hibernate 需要 4-5 秒。添加在上面和 BAM :)
【解决方案2】:

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

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

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

select * 

来自 ( 选择 this.a、this.b、this.state、this.id 从 view_user 这个 this.state=:1 order by this.a asc, this.b ) 其中 rownum

【讨论】:

  • 抱歉错字。查询已被简化,但逻辑上仍然相同。由于 select 中的列列表,hibernate 看起来有点复杂。
  • np。 VIEW_USER.USER_STATE_ID 上是否有索引?
  • 是的,有。 state_id 是表的外键,其中 state_id 是主键。
【解决方案3】:

问题的最终解决方案是 c3p0。我们无法追查为什么选择了错误的查询计划。我们最后的假设是它与 dbcp 的连接初始化有关。但是因为时间的缘故,我们尝试了c3p0。初体验:

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

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

感谢所有输入!

【讨论】:

    【解决方案4】:

    经过大量调试和尝试,我有了解决问题的答案。但是,我还不能解释到底是什么问题。

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

    • initialSize=0
    • minIdle=10

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

    我目前正在分析日志。在 DB 服务器上,跟踪文件没有显示任何差异。到目前为止,JDBC 跟踪也没有显示任何有趣的信息。

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

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

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

    【讨论】:

    • 好的,解决方法是不行的。在开始时初始化的所有连接(由于 initialSize)都可以,并且会导致快速查询,即采用正确的查询计划。但是一旦因为启动时没有创建足够的连接而创建了新连接,这些新创建的连接就会出现性能问题。
    【解决方案5】:

    上面/下面有一个关于 nvarchars 的答案以及 SQL Server 的设置 - 这是 Oracle 的类似设置...

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

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

    【讨论】:

      猜你喜欢
      • 2012-08-20
      • 1970-01-01
      • 2015-06-14
      • 1970-01-01
      • 2021-12-17
      • 2015-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多