mysql 1337 游标嵌套_jdbc - java.sql.SQLException: - ORA-01000:超出最大打开游标数

ORA-01000是最大打开游标错误,是Oracle数据库开发中极为常见的错误。 在Java的上下文中,当应用程序尝试打开更多ResultSet而不是数据库实例上的已配置游标时,会发生这种情况。

常见原因是:

配置错误

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

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

解:

增加数据库上的游标数(如果资源允许)或

减少应用程序中的线程数。

光标泄漏

应用程序未关闭ResultSets(在JDBC中)或游标(在数据库中的存储过程中)

解决方案:游标泄漏是错误; 增加数据库上的游标数量只会延迟不可避免的失败。 可以使用静态代码分析,JDBC或应用程序级日志记录以及数据库监视找到泄漏。

背景

本节介绍了游标背后的一些理论以及如何使用JDBC。 如果你不需要知道背景,你可以跳过这个并直接进入'消除泄漏'。

什么是光标?

游标是数据库上保存查询状态的资源,特别是读者在ResultSet中的位置。 每个SELECT语句都有一个游标,PL / SQL存储过程可以打开并使用所需数量的游标。 您可以在Orafaq上找到有关游标的更多信息。

数据库实例通常提供多个不同的模式,许多不同的用户每个都有多个会话。 为此,它具有可用于所有模式,用户和会话的固定数量的游标。 当所有游标都打开(正在使用中)并且请求进入需要新游标时,请求将失败,并显示ORA-010000错误。

查找并设置游标数量

该号码通常由DBA在安装时配置。 可以在Oracle SQL Developer的Administrator函数中访问当前使用的游标数,最大数量和配置。 从SQL可以设置为:

ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;

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

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

JDBC Connection是数据库会话的客户端表示,并提供数据库事务。 连接一次只能打开一个事务(但事务可以嵌套)

数据库上的单个游标支持JDBC ResultSet。 在ResultSet上调用close()时,将释放游标。

JDBC CallableStatement调用数据库上的存储过程,通常用PL / SQL编写。 存储过程可以创建零个或多个游标,并且可以将游标作为JDBC ResultSet返回。

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

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

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

关闭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) { }

}

注意finally子句如何忽略close()引发的任何异常:

如果只是在没有try {} catch {}的情况下关闭ResultSet,它可能会失败并阻止Statement被关闭

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

在Java 7中,Oracle引入了AutoCloseable接口,该接口用一些很好的语法糖代替了大部分Java 6样板。

持有JDBC对象

JDBC对象可以安全地保存在局部变量,对象实例和类成员中。 通常更好的做法是:

使用对象实例或类成员来保存在较长时间内多次重用的JDBC对象,例如Connections和PreparedStatements

为ResultSets使用局部变量,因为这些变量通常在单个函数的范围内获取,循环然后关闭。

但是有一个例外:如果您使用的是EJB或Servlet / JSP容器,则必须遵循严格的线程模型:

只有Application Server创建线程(用于处理传入请求)

只有Application Server创建连接(您从连接池获取)

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

特别是,永远不要在不同的远程调用上保存JDBC对象(Connections,ResultSets,PreparedStatements等) - 让Application Server管理它。 Application Server不仅提供连接池,还缓存PreparedStatements。

消除泄漏

有许多流程和工具可用于帮助检测和消除JDBC泄漏:

在开发过程中 - 早期捕获bug是迄今为止最好的方法:

开发实践:良好的开发实践应该在软件离开开发人员的桌面之前减少其中的错误数量。 具体做法包括:

结对编程,教育没有足够经验的人

代码审查,因为许多眼睛比一个更好

单元测试意味着您可以使用测试工具来执行任何和所有代码库,这使得重现泄漏变得微不足道

使用现有库进行连接池而不是构建自己的库

静态代码分析:使用优秀的Findbugs之类的工具来执行静态代码分析。 这会占用许多未正确处理close()的地方。 Findbugs有一个Eclipse插件,但它也可以单独运行,可以集成到Jenkins CI和其他构建工具中

在运行时:

可持续性和提交

如果ResultSet可保持性为ResultSet.CLOSE_CURSORS_OVER_COMMIT,则在调用Connection.commit()方法时将关闭ResultSet。 这可以使用Connection.setHoldability()或使用重载的Connection.createStatement()方法设置。

在运行时记录。

在代码中添加好的日志语句。 这些应该清晰易懂,以便客户,支持人员和团队成员无需培训即可理解。 它们应该简洁并包括打印关键变量和属性的状态/内部值,以便您可以跟踪处理逻辑。 良好的日志记录是调试应用程序的基础,尤其是已部署的应用程序。

您可以向项目添加调试JDBC驱动程序(用于调试 - 不实际部署它)。 一个例子(我没有用过它)是log4jdbc。 然后,您需要对此文件进行一些简单的分析,以查看哪些执行没有相应的关闭。 如果存在潜在问题,应该突出显示打开和关闭

监控数据库。 使用SQL Developer'Monitor SQL'功能或Quest的TOAD等工具监控正在运行的应用程序。 本文介绍了监视。 在监视期间,您查询打开的游标(例如,从表v $ sesstat)并查看其SQL。 如果游标数量在增加,并且(最重要的是)由一个相同的SQL语句控制,那么您就知道该SQL存在泄漏。 搜索您的代码并进行审核。

其他想法

你可以使用WeakReferences来处理关闭连接吗?

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

如果将构造函数中的ReferenceQueue传递给soft或weak Reference,则当对象发生GC时(如果它完全发生),该对象将被放置在ReferenceQueue中。 使用此方法,您可以与对象的最终化进行交互,然后您可以在此时关闭或完成对象。

幽灵参考有点怪异; 它们的目的只是控制最终化,但是你永远无法获得对原始对象的引用,因此很难在它上面调用close()方法。

但是,尝试控制GC的运行时间很少是个好主意(Weak,Soft和PhantomReferences会在对象排入GC之后让您知道)。 实际上,如果JVM中的内存量很大(例如-Xmx2000m),您可能永远不会GC该对象,您仍然会遇到ORA-01000。 如果JVM内存相对于程序的要求较小,您可能会发现ResultSet和PreparedStatement对象在创建后立即被GCed(在您可以读取它们之前),这可能会使您的程序失败。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值