Ora-01000是最大的打开游标错误,是Oracle数据库开发中非常常见的错误.在Java上下文中,当应用程序试图打开比数据库实例上配置的游标更多的ResultSets时,就会发生这种情况。

共同的原因是:配置错误在数据库上(如果资源允许)或

减少应用程序中的线程数。您的应用程序中查询数据库的线程比DB上的游标多。一种情况是,连接和线程池大于数据库上的游标数。

您有许多开发人员或应用程序连接到同一个DB实例(其中可能包含许多模式),而且您使用的连接太多了。

解决办法:

光标泄漏应用程序没有关闭ResultSets(在JDBC中)或游标(在数据库中的存储过程中)

解游标泄漏是错误;增加DB上游标的数量只会延迟不可避免的故障。可以使用或应用程序级日志记录,以及

背景

本节描述了游标背后的一些理论以及如何使用JDBC。如果你不需要知道背景,你可以跳过这个,直接去“消除泄漏”。

什么是光标?

游标是数据库中保存查询状态的资源,特别是读取器在ResultSet中的位置。每个SELECT语句都有一个游标,PL/SQL存储过程可以根据需要打开和使用任意数量的游标。您可以在奥拉法克.

数据库实例通常提供几种不同的服务。图式,许多不同的用户每一个多次会话..为此,它有固定数量的游标可供所有架构、用户和会话使用。当所有游标都打开(正在使用),并且请求中需要一个新的游标时,请求就会失败,出现Ora-010000错误。

查找和设置游标数

数字通常由安装时的DBA配置。中管理员函数中当前使用的游标数、最大数量和配置。Oracle SQL Developer..在SQL中,它可以用以下方式设置:ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;

将JVM中的JDBC与DB上的游标关联起来

下面的JDBC对象紧密耦合到以下数据库概念:JDBC

连接是数据库的客户端表示形式。

会议并提供数据库

交易..连接在任何时候只能打开一个事务(但是事务可以嵌套)

JDBC

结果集由单个

光标在数据库上。当在ResultSet上调用Close()时,游标将被释放。

JDBC

呼叫状态调用

存储过程在数据库上,通常用PL/SQL编写。存储过程可以创建零或多个游标,并且可以JDBCResultSet的形式返回游标。

JDBC是线程安全的:可以在线程之间传递各种JDBC对象。

例如,可以在一个线程中创建连接;另一个线程可以使用此连接创建PreparedStatement,第三个线程可以处理结果集。唯一的主要限制是在任何时候都不能在单个PreparedStatement上打开多个ResultSet。看见Oracle DB是否支持每个连接的多个(并行)操作?

请注意,数据库提交发生在连接上,因此该连接上的所有DML(INSERT、UPDATE和DELETE)都将一起提交。因此,如果要同时支持多个事务,则每个并发事务必须至少有一个连接。

关闭JDBC对象

执行ResultSet的一个典型例子是:

Statement stmt = conn.createStatement();try {
ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" );
try {
while ( rs.next() ) {
System.out.println( "Name: " + rs.getString("FULL_NAME") );
}
} finally {
try { rs.close(); } catch (Exception ignore) { }
}} finally {
try { stmt.close(); } catch (Exception ignore) { }}

注意Final子句如何忽略CLOSE()引发的任何异常:如果您只是关闭ResultSet而不带try{}Catch{},它可能会失败,并阻止关闭该语句。

我们希望允许在尝试体中引发的任何异常传播到调用方。如果您有一个循环,例如,创建和执行语句,请记住关闭循环中的每个语句。

在Java 7中,Oracle引入了自动关闭接口它用一些很好的语法糖取代了大多数Java 6样板。

保存JDBC对象

JDBC对象可以安全地保存在局部变量、对象实例和类成员中。一般来说,更好的做法是:使用对象实例或类成员来保存在较长时间内多次重用的JDBC对象,例如连接和PreparedStatement

对ResultSets使用局部变量,因为这些变量是在单个函数的范围内获得、循环并关闭的。

但是,有一个例外:如果使用EJB或servlet/JSP容器,则必须遵循严格的线程模型:只有ApplicationServer创建线程(用于处理传入请求)

只有ApplicationServer创建连接(从连接池中获得)

在调用之间保存值(状态)时,必须非常小心。永远不要将值存储在自己的缓存或静态成员中-这在集群和其他奇怪的条件下是不安全的,而且ApplicationServer可能会对您的数据做可怕的事情。相反,使用有状态bean或数据库。

特别是,

绝不可能在不同的远程调用中保存JDBC对象(连接、结果集、PreparedStatement等)-让ApplicationServer来管理它。ApplicationServer不仅提供连接池,还缓存PreparedStatement。

消除泄漏

有许多可用于帮助检测和消除JDBC泄漏的流程和工具:在开发过程中尽早捕获bug是目前为止最好的方法:教育那些没有足够经验的人

因为很多眼睛都比一只眼睛好

这意味着您可以使用测试工具中的任何和所有代码库,这使得重新生成泄漏变得非常简单。

使用用于连接池,而不是构建自己的连接池。开发实践:良好的开发实践应该在软件离开开发人员的办公桌之前减少软件中的bug数量。具体做法包括:

静态代码分析:使用优秀的工具发现虫若要执行静态代码分析,请执行以下操作。这就引出了许多没有正确处理CLOSE()的地方。Findbug为Eclipse提供了一个插件,但它也为一次性运行独立的插件,还集成到Jenkins CI和其他构建工具中。

在运行时:将良好的日志语句放入代码中。这些应该是明确和可以理解的,这样客户、支持人员和队友就可以不经过培训就能理解。它们应该简洁,包括打印关键变量和属性的状态/内部值,以便跟踪处理逻辑。良好的日志记录对于调试应用程序至关重要,特别是那些已经部署的应用程序。

您可以将调试JDBC驱动程序添加到项目中(用于调试-不要实际部署它)。有一个例子(我没有用过)是log4jdbc..然后,您需要对该文件进行一些简单的分析,以查看哪些执行没有相应的关闭。如果存在潜在的问题,计数打开和关闭应该突出显示。监视数据库。使用SQLDeveloper‘Monitor SQL’函数或..监控在..在监视期间,查询打开的游标(如表v$sesstat中的游标)并检查它们的SQL。如果游标的数量在增加,并且(最重要的是)被一个相同的SQL语句所主导,那么您就知道该SQL存在漏洞。搜索您的代码并检查。如果ResultSet持久性为ResultSet.CLOSE_CURSORS_Over_COMMIT,那么当调用Connection.COMMIT()方法时,ResultSet将关闭。这可以使用Connection.setHolability()或使用重载的Connection.createStatement()方法来设置。可持续能力与承诺

在运行时进行日志记录。

其他思想

您能使用WeakReferences来处理关闭的连接吗?

弱引用和软引用是允许您以允许JVM在它认为合适的任何时候垃圾收集引用的方式引用对象的方法(假设该对象没有强引用链)。

如果将构造函数中的ReferenceQueue传递给软引用或弱引用,则当对象发生时(如果发生的话),该对象将被放置在ReferenceQueue中。使用这种方法,您可以与对象的终结进行交互,并且可以在此时关闭或完成该对象。

幻影引用有点奇怪;它们的目的只是控制终结,但您永远无法获得对原始对象的引用,因此很难在其上调用CLOSE()方法。

然而,尝试控制GC运行时很少是一个好主意(弱、软和幻影引用会让您知道)事后该对象将排队等待GC)。实际上,如果jvm中的内存量很大(例如-xmx2000m),您可以绝不可能GC对象,您仍将体验Ora-01000.如果JVM内存相对于程序的需求很小,您可能会发现ResultSet和PreparedStatement对象在创建之后立即被GCed(在从它们读取之前),这很可能会导致程序失败。

TL;DR:弱引用机制不是管理和关闭语句和ResultSet对象的好方法。