首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JDBC仪表和ORA-01000:超出最大打开游标

JDBC仪表和ORA-01000:超出最大打开游标
EN

Stack Overflow用户
提问于 2016-10-12 22:03:37
回答 1查看 1.4K关注 0票数 2

当连接是创建的关闭的时,我试图更好地使用Tomcat连接池中的哪些web应用程序使用Oracle (11g)连接;这样,我们就可以通过监视V$SESSION表来查看应用程序正在使用的连接。这是可行的,但是由于添加了这个“工具”,我看到ORA-01000: maximum open cursors exceeded错误正在被记录,并且在负载测试期间注意到一些连接被从池中删除(这可能是好的,因为我已经启用了testOnBorrow,所以我假设连接被标记为无效并从池中删除)。

我花了一周的大部分时间在互联网上寻找可能的答案。下面是我尝试过的内容(经过一段时间后,所有结果都会导致打开游标错误).

下面的方法都是用同样的方式..。

论创造

  1. 我们从池中获得一个连接
  2. 我们调用一个执行以下代码的方法,传递web应用程序的上下文名称

近距离

  1. 我们关闭了连接(返回到池)
  2. 在对连接发出close()之前,我们调用一个执行下面代码的方法,将"Idle“作为要存储在V$SESSION中的名称传入

方法1:

代码语言:javascript
复制
CallableStatement cs = connection.prepareCall("{call DBMS_APPLICATION_INFO.SET_MODULE(?,?)}");
try {
    cs.setString(1, appId);
    cs.setNull(2, Types.VARCHAR);
    cs.execute();
    log.trace(">>> Executed Oracle DBMS_APPLICATION_INFO.SET_MODULE with module_name of '" + appId + "'");
} catch (SQLException sqle) {
    log.error("Error trying to call DBMS_APPLICATION_INFO.SET_MODULE('" + appId + "')", sqle);
} finally {
    cs.close();
}

方法2:

我升级到了12cOJDBC驱动程序(ojdbc7),并在连接上使用了本机setClientInfo方法.

代码语言:javascript
复制
// requires ojdbc7.jar and oraclepki.jar to work (setEndToEndMetrics is deprecated in ojdbc7)
connection.setClientInfo("OCSID.CLIENTID", appId);

方法3:

我目前正在使用这种方法。

代码语言:javascript
复制
String[] app_instrumentation = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
app_instrumentation[OracleConnection.END_TO_END_CLIENTID_INDEX] = appId;
connection.unwrap(OracleConnection.class).setEndToEndMetrics(app_instrumentation, (short)0);
// in order for this to be sent, a query needs to be sent to the database - this works fine when a 
// connection is created, but when it is closed, we need a little something to get the change into the db
// try using isValid()
connection.isValid(1);

方法4:

代码语言:javascript
复制
String[] app_instrumentation = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
app_instrumentation[OracleConnection.END_TO_END_CLIENTID_INDEX] = appId;
connection.unwrap(OracleConnection.class).setEndToEndMetrics(app_instrumentation, (short)0);
// in order for this to be sent, a query needs to be sent to the database - this works fine when a 
// connection is created, but when it is closed, we need a little something to get the change into the db
if ("Idle".equalsIgnoreCase(appId)) {
    Statement stmt = null;
    ResultSet rs = null;
    try {
        stmt = connection.createStatement();
        rs = stmt.executeQuery("select 1 from dual");
    } finally {
        if (rs != null) {
            rs.close();
        }
        if (stmt != null) {
            stmt.close();
        }
    }
}

当我查询打开的游标时,我注意到在池中使用的帐户上返回了以下SQL (对于池中的每个连接).

代码语言:javascript
复制
select NULL NAME, -1 MAX_LEN, NULL DEFAULT_VALUE, NULL DESCR

这在我们的代码中没有显式存在,所以我只能假设它来自运行验证查询(select 1 from dual)时的池,或者来自setEndToEndMetrics方法(或者来自DBMS_APPLICATION_INFO.SET_MODULE proc,或者来自isValid()调用)。我试图在方法1和方法4中显式地创建和关闭Statement (CallableStatement)和ResultSet对象,但它们没有区别。

我不想增加允许游标的数量,因为这只会拖延不可避免的事情(直到我在“工具”中添加之前,我们从未遇到过这个问题)。

我已经阅读了优秀的post here (java.sql.SQLException:- ORA-01000:超出了最大打开游标),但我肯定还遗漏了一些东西。任何帮助都将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-10-14 13:13:48

普尔先生的声明:“这个查询看起来像是在获取假的元数据”,这在我的脑海中敲响了一个铃铛。

我开始怀疑,这是否是在池的数据源的testOnBorrow属性上运行的验证查询的未知残留物(尽管验证查询被定义为select 1 from dual)。我将其从配置中删除,但没有任何效果。

然后,我尝试删除在V$SESSION中设置客户端信息的代码(上面的方法3);Oracle继续显示不寻常的查询,几分钟后,会话将达到最大打开游标限制。

然后,我发现DAO类中有一个"logging“方法,它记录了来自connection对象的一些元数据(当前自动提交、当前事务隔离级别、JDBC驱动程序版本等设置的值)。在此日志记录中,对getClientInfoProperties()对象使用了DatabaseMetaData方法。当我查看这个方法的JavaDocs时,这个异常查询的来源变得非常清楚,请检查它.

代码语言:javascript
复制
ResultSet java.sql.DatabaseMetaData.getClientInfoProperties() throws SQLException

Retrieves a list of the client info properties that the driver supports. The result set contains the following columns 

1. NAME String=> The name of the client info property
2. MAX_LEN int=> The maximum length of the value for the property
3. DEFAULT_VALUE String=> The default value of the property
4. DESCRIPTION String=> A description of the property. This will typically contain information as to where this property is stored in the database. 

The ResultSet is sorted by the NAME column 

Returns:

A ResultSet object; each row is a supported client info property

您可以清楚地看到,不寻常的查询(select NULL NAME, -1 MAX_LEN, NULL DEFAULT_VALUE, NULL DESCR)与JavaDocs对DatabaseMetaData.getClientInfoProperties()方法的描述相匹配。哇,对!?

这是执行函数的代码。据我所知,从“关闭ResultSet”的角度看,它看起来是正确的--不确定将保持ResultSet打开的是什么--它显然是在finally块中关闭的。

代码语言:javascript
复制
log.debug(">>>>>> DatabaseMetaData Client Info Properties (jdbc driver)...");
ResultSet rsDmd = null;
try {
    boolean hasResults = false;
    rsDmd = dmd.getClientInfoProperties();
    while (rsDmd.next()) {
        hasResults = true;
        log.debug(">>>>>>>>> NAME = '" + rsDmd.getString("NAME") + "'; DEFAULT_VALUE = '" + rsDmd.getString("DEFAULT_VALUE") + "'; DESCRIPTION = '" + rsDmd.getString("DESCRIPTION") + "'");
    }
    if (!hasResults) {
        log.debug(">>>>>>>>> DatabaseMetaData Client Info Properties was empty (nothing returned by jdbc driver)");
    }
} catch (SQLException sqleDmd) {
    log.warn("DatabaseMetaData Client Info Properties (jdbc driver) not supported or no access to system tables under current id");
} finally {
    if (rsDmd != null) {
        rsDmd.close();
    }
}

查看日志,当使用Oracle连接时,将记录>>>>>>>>> DatabaseMetaData Client Info Properties was empty (nothing returned by jdbc driver)行,因此不会抛出异常,但也不会返回任何记录。我只能假设ojdbc6 (11.2.0.x.x)驱动程序不正确地支持getClientInfoProperties()方法--很奇怪(我认为)没有抛出异常,因为查询本身缺少FROM关键字(例如,在蟾蜍中执行时不会运行)。无论如何,ResultSet至少应该被关闭(虽然连接本身仍在使用中--也许这会导致ResultSet关闭也不会释放游标)。

因此,我所做的所有工作都是在一个分支中进行的(我在对我在主干中工作的最初问题的评论中提到--我的错误--我在一个已经创建的分支中,认为它是基于主干代码而不是修改的--我没有在这里进行尽职调查),所以我检查了SVN提交历史,发现这个附加的日志功能是几周前一个同事添加的(幸运的是,它没有被提升到主干或更高的环境--请注意,这段代码对我们的Sybase数据库很好)。我从SVN分支得到的更新带来了他的代码,但我从未真正注意到更新的内容(我的错误)。我和他谈到了这段代码对Oracle做了什么,我们同意将代码从日志记录方法中删除。在我们的开发环境中,我们还进行了一次检查,只记录连接元数据(他说他添加了这段代码,以帮助解决一些驱动程序版本和自动提交问题)。一旦完成,我就能够在没有任何游标问题的情况下运行负载测试(成功!)。

无论如何,我想回答这个问题,因为当我搜索select NULL NAME, -1 MAX_LEN, NULL DEFAULT_VALUE, NULL DESCRORA-01000 open cursors时,没有返回可信的点击(返回的大多数点击都是为了确保关闭连接资源,即ResultSets、Statements等)。我认为这表明通过JDBC对Oracle进行的数据库元数据查询是ORA-01000错误的罪魁祸首。我希望这对其他人有帮助。谢谢。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40009130

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档