Hibernate + Java 性能较慢,但当我将 TOAD 与相同的本机 Oracle 查询一起使用时性能较快

Posted

技术标签:

【中文标题】Hibernate + Java 性能较慢,但当我将 TOAD 与相同的本机 Oracle 查询一起使用时性能较快【英文标题】:Slow performance on Hibernate + Java but fast when I use TOAD with the same native Oracle query 【发布时间】:2012-12-27 00:19:16 【问题描述】:

我检测到 Oracle 上的休眠和本机查询存在性能问题。当我在 TOAD 上执行带有多个参数的复杂 SQL 查询时,我会以毫秒为单位得到结果。但是,当我使用 Hibernate 执行相同的查询时,这个时间会大大增加(最多四秒甚至更多)。

我的 SQL 查询相当复杂,返回一个唯一值(因此,问题与安装类所需的时间无关),它包含多个格式为“:nameParameter”的参数。此查询存储在字符串中。例如,

String myNamedNativeQuery = "select count(*) from tables "+
                            "where column1 = :nameParameter1 "+ 
                            "and column2 = :nameParameter2";  
                            //actually my sentence is much more complex!!

当我在 TOAD 上执行句子时,它会在几毫秒内得到解决。但是在 Hibernate 中使用这句话

SQLQuery query = session.createSQLQuery("myNamedNativeQuery");

query.setParameter(nameParameter1, value1);
query.setParameter(nameParameter2, value2);

query.uniqueResult(); 

需要几秒钟才能获得相同的结果。

我意识到如果我直接在本机查询上替换参数,然后使用 Hibernate 执行语句,时间会大大减少。应该是这样的:

String strQuery = session.getNamedQuery("myNamedNativeQuery").getQueryString();

myNamedNativeQuery = myNamedNativeQuery.replace("nameParameter1", value1);
myNamedNativeQuery = myNamedNativeQuery.replace("nameParameter2", value2);

SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.uniqueResult(); 

有人知道发生了什么吗??

提前致谢。

PS:Oracle版本是9i和Hibernate 3.2

【问题讨论】:

在休眠时打开“show sql”选项。它会向您显示 hibernate 正在运行的查询,并让您深入了解为什么它需要这么长时间。 【参考方案1】:

我认为这段代码发生了什么:

SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.setParameter(nameParameter1, value1);
query.setParameter(nameParameter2, value2);
query.uniqueResult(); 

这是:

在第 1 行:根据命名参数的一些预期值创建查询计划。

在第 4 行:使用 value1 和 value2 执行查询,但这些值不是第 1 行详细说明的查询计划的“好值”,因此,数据库正在为实际值执行一个非常不合适的计划而且需要很多时间。

为什么?

查看HibernateSessionImpl.createSQLQuery(...)的源码找到了这行代码:

SQLQueryImpl query = new SQLQueryImpl(
                sql,
                        this,
                        factory.getQueryPlanCache().getSQLParameterMetadata( sql )
        );

使用一些参数元数据调用getQueryPlanCache()。我认为这个元数据不够好

【讨论】:

有趣...那么,在这些情况下,最佳做法是什么?也许在调用 createSQLQuery() 之前用它们的值替换参数? 也许自适应游标共享会有所帮助。但该功能直到 11g 才可用。 根据我的测试,这种情况也使用 session.getNamedQuery('myNamedQuery') 发生。似乎 Hibernate 正在缓存查询计划,并且使用某些参数选择的查询计划不是最好的。那么,是否可以配置查询计划缓存? 您写道“时间急剧减少”,所以我不明白您为什么现在说您的第二个解决方案效率不高?你能详细说明一下吗? 在我的项目中,我有很多 HQL 和本地 SQL 命名查询存储到 XML 中。它们中的大多数被执行为 session.getNamedQuery('namedQuery').setParameter(...).uniqueResult();如果为命名查询计算查询计划,然后我执行相同的命名查询,但使用其他参数,查询计划不可能是最好的,可以吗?那么,是不是可以用参数值来细化查询计划呢?【参考方案2】:

我给你的答案是:

移除所有绑定参数,使用 StatelessSession 代替 Session

使用 SQLQuery 代替包含参数值的完整 SQL 查询

StatelessSession session = sessionFactory.openStatelessSession();

我有类似的问题,直到我得到更好的解决方案,这就是我设法使它工作的原因。 见Hibernate parameterized sql query slow and active oracle sessions

【讨论】:

【参考方案3】:

<property name = "hibernate.temp.use_jdbc_metadata_defaults">false</property>

将此添加到您的 hibernate.cfg.xml 或更新您的应用程序属性文件。

【讨论】:

以上是关于Hibernate + Java 性能较慢,但当我将 TOAD 与相同的本机 Oracle 查询一起使用时性能较快的主要内容,如果未能解决你的问题,请参考以下文章

大堆的 Java 速度较慢,没有过多的 gc-ing

Hibernate框架入门

Java程序员注意——扼杀性能的 10 个常见 Hibernate 错误

Hibernate、JDBC 和 Java 在大中型结果集上的性能

Couchbase 集群中较慢的插入性能

与 Room 相比,SQLDelight 性能较慢