我们有一个在生产环境中运行的Web应用程序,在某些时候客户抱怨应用程序的速度有多慢。
当我们检查应用程序和数据库发生了什么时,我们发现了几个用户同时执行的这个“宝贵”查询(从而对数据库服务器造成了极高的负载):
SELECT NULL AS table_cat,
o.owner AS table_schem,
o.object_name AS table_name,
o.object_type AS table_type,
NULL AS remarks
FROM all_objects o
WHERE o.owner LIKE :1 ESCAPE :"SYS_B_0" AND
o.object_name LIKE :2 ESCAPE :"SYS_B_1" AND
o.object_type IN(:"SYS_B_2", :"SYS_B_3")
ORDER BY table_type, table_schem, table_name
我们的应用程序不执行此查询,我相信它是一个Hibernate内部查询。我发现很少有关于为什么Hibernate会执行这个极其繁重的查询的信息,所以任何有关如何避免它的帮助都非常感谢!
生产环境信息:Red Hat Enterprise Linux 5.3(Tikanga),JDK 1.5,Web容器OC4J(whitin Oracle Application Server),Oracle Database 10.1.0.4,JDK 1.2和1.3的JDBC驱动程序,Hibernate 3.2.6版。 ga,连接池库C3P0版本0.9.1。
更新 :感谢@BalusC for claryfing确实是执行查询的Hibernate,现在我对正在发生的事情有了更好的了解。我将解释我们处理hibernate会话的方式(这是非常基本的,如果你有关于如何更好地处理它的建议,那么它们非常受欢迎!)
我们有一个过滤器(实现javax.servlet.Filter),当它启动时(init方法)它构造会话工厂(通常这只发生一次)。然后,进入应用程序的每个HttpRequest都会通过过滤器并获得 new 会话,并启动一个事务。当它结束的过程时,它通过过滤器返回,进行事务的提交,杀死 hibernate会话,然后继续前进页面(我们不在Http中存储hibernate会话)会话,因为它在我们的测试中从未运作良好)。
现在,我认为这个问题就出现了。在我们的开发环境中,我们在Tomcat 5.5中部署我们的应用程序,当我们启动该服务时,所有过滤器都会立即启动,只启动一次。在使用OC4J的生产环境中似乎没有那种方式。我们部署应用程序,并且只有在第一个请求到达时,OC4J才会实例化过滤器。
这让我认为OC4J在每个请求上实例化过滤器(或至少多次,这仍然是错误的),从而在每个请求上创建一个会话工厂,执行%& amp ;%#%$#query,这导致了我的问题!
现在,这是正确的吗?我有一种方法可以配置OC4J,使其仅实例化一次过滤器吗?
非常感谢大家花时间回复此事!
答案 0 :(得分:3)
它确实来自Hibernate,特别是org.hibernate.tool.hbm2ddl.TableMetadata
。每个都用于验证模式(表和列映射)。显然,它不必要地在每个生成的请求或会话上执行,而不是仅在应用程序启动期间执行一次。例如,您是否在每个请求或会话上不必要地调用Hibernate Configurator?
答案 1 :(得分:2)
正如@BalusC所指出的,此查询是在架构验证期间执行的。但是,在创建SessionFactory
(如果已激活)时,通常会对所有人进行一次验证。您是否明确地使用以下方法:Configuration#validateSchema(Dialect, DatabaseMetadata)
?
现在,这是正确的吗?我有一种方法可以配置OC4J,使其仅实例化一次过滤器吗?
您在View中打开会话的实现看起来很好(并且与this page中建议的非常接近)。根据Servlet规范,部署描述符中每个<filter>
声明只有一个实例按容器的Java虚拟机(JVMTM)进行实例化。由于OC4J不太可能出现这种情况,我很想说还有别的东西。
您可以在过滤器中添加一些日志吗?如何使SessionFactory
静态(在一个好的HibernateUtil
类中)?
答案 2 :(得分:2)
好吧,经过几个月的观察,事实证明问题不是我的网络应用程序。问题是使用数据库的相同实例(不同用户)的其他Oracle Forms应用程序。
发生的事情是Oracle Forms应用程序锁定了数据库上的记录,因此几乎所有数据库的工作都非常慢(包括我心爱的Hibernate查询)。
锁定的原因是Oracle Forms应用程序的所有外键都没有编入索引。因此,当我的老板向我解释时(他发现原因)当用户在Oracle Form应用程序中编辑主 - 详细信息关系的主记录时,数据库会锁定整个详细信息表不是其外键的索引。这是因为Oracle Forms的工作方式,它会更新主记录的所有字段,包括主键的字段,这些是外键引用的字段。
简而言之,请永远不要留下没有索引的外键。我们遭受了很多苦难。
感谢所有花时间提供帮助的人。
答案 3 :(得分:2)
具体来说,编写支持不同数据库的软件的人们以数据库中立的方式打包他们的软件。即。当不存在覆盖时,他们所做的是使用jdbc db metadata getTables调用来检查连接是否仍然有效。通常,您使用select * from dual etc覆盖,但是当没有完成或者您没有具体说明您正在使用什么类型的数据库时,编写的软件将运行可以与任何JDBC驱动程序一起使用的东西。 jdbc db metadatabase getTables会这样做。
答案 4 :(得分:2)
我只是想介绍一下我以前解决这个问题的方法。我们的数据库中通常有很多模式,这需要几个小时才能完成我们尝试使用的应用程序,因为它最终检查了大量的对象(查询本身会快速执行,但它只是做了这么多人。)
我所做的是覆盖所连接架构中的ALL_OBJECTS视图,以便它只返回它自己的对象而不是db中的所有对象。
e.g。
创建或替换视图ALL_OBJECTS作为SELECT USER OWNER,O。*来自USER_OBJECTS O;
这不是最好的解决方案,但对于这个应用程序,没有其他任何东西可以使用ALL_OBJECTS视图,因此它可以正常工作并且启动速度更快。
答案 5 :(得分:1)
10g数据库中的sys架构是否使用更新的统计信息进行分析?您是否收集了sys架构中固定表的统计信息。 all_objects上的查询不应该对系统造成负担。如果您通过autotrace / tkprof运行查询,那么资源的主要部分是什么/哪里。
答案 6 :(得分:1)
这来自默认的C3PO测试查询。在配置中提供更简单的查询。有点像,从双重选择'X'。
答案 7 :(得分:1)
出现同样的问题,原因正是Bob Breitling所描述的那个,C3P0默认使用JDBC API进行连接测试:
java.sql.DatabaseMetaData#getTables(....)
为了改变这种行为,必须设置 preferredTestQuery ,或者如果通过hibernate使用C3P0 - hibernate.c3p0.preferredTestQuery
答案 8 :(得分:0)
我相信这个查询来自Oracle JDBC驱动程序,以实现Hibernate请求,通过DatabaseMetaData检索数据库对象信息。
这个查询不应该太贵,或者至少不在我方便的系统上。你对all_objects的评价是多少,更重要的是,你在解释计划的行/字节总数中看到了什么?