tomcat 连接池没有关闭连接?

tomcat connection pool does not close connection?

我 运行 使用 ORA Cursor exceed 第三方应用程序使用 tomcat 连接池的问题。

除了解决这些问题,我们还想通过设置释放空闲会话:

minEvictableIdleTimeMillis =  60000
timeBetweenEvictionRunsMillis = 60000

但不知何故,这些会话似乎根本没有释放(即使没有任何流量):

select  a.value, s.username, s.sid, s.serial#, s.machine, to_char(cast(s.logon_time as date),'hh24:mi:ss') as activesince from v$sesstat a, v$statname b, v$session s where a.statistic# = b.statistic#  and s.sid=a.sid and b.name = 'opened cursors current' and s.username = 'username' order by value desc;

这显示了我 "old" session/cursors 和绝对超过 1 分钟的陈述。

我是否缺少其他选项? 感谢和欢呼,E.

这是一个非常棘手的话题

前言

连接池的设计,一方面是为了限制数据库同时打开的最大连接数,另一方面是为了重用池中打开的连接。

建立与数据库的物理连接需要时间。这次是 'saved' 通过保持连接打开(在池中)。

连接处理

before java 7、使用后一定要关闭连接。大部分在 finally 块中:

Connection conn = [retrieve DB-Connection];
try {
    // do something
} catch (SQLException e) {
    // handle exception
} finally {
    conn.close();
}

结合连接池,连接在物理上并没有关闭,只是释放到连接池中,在Statements和ResultSets关闭后,可以重复使用。

因为 java 7,上面的代码 should/can 看起来像这样:

try (Connection conn = [retrieve DB-Connection]) {
    // do something
} catch (SQLException e) {
    // handle exception
}

这是新的 "try-catch with resources" 功能。无论何时离开 try 块,try 括号内的资源都会关闭(可自动关闭)。括号可以包含几个以分号分隔的可自动关闭的资源。

try (Connection conn = [retrieve DB-Connection];
     Statement stat = conn.createStatement();
     ResultSet result = stat.executeQuery("SELECT 1 FROM DUAL")) {
    // do something
} catch (SQLException e) {
    // handle exception
}

如果连接不是 closed/released 手动或由 "try-catch with resources" 处理,连接池有一个可配置的后备功能 -

the abandoning feature
处理 unclosed/unreleased (已放弃)连接。

请参阅 tomcat-版本相关的 "JNDI-Resources how to" -> "JDBC Data Sources" 文档。此 link 与版本 9.0

相关

The abandoning feature is disabled by default and can be configured using the following properties:

  • removeAbandoned - true or false: whether to remove abandoned connections from the pool. Default: false
  • removeAbandonedTimeout - The number of seconds after which a borrowed connection is assumed to be abandoned. Default: 300
  • logAbandoned - true or false: whether to log stack traces for application code which abandoned a statement or connection. This adds serious overhead. Default: false

游标和 "ORA Cursor exceed"-Exception

最大打开游标为可变数据库-属性.

因此,通过在一个连接中执行大量查询而不关闭 'previous' opened/created 结果集和语句,可能会发生 ORA Cursor exceed 异常

以下示例导致打开五个游标。在这种情况下,使用带有资源的 try-catch,并且 ResultSets、Statements 和 Connection 通过离开 try-block:

自动关闭
try (Connection conn = [retrieve DB-Connection];
     Statement stat0 = conn.createStatement();
     ResultSet result0 = stat0.executeQuery(...);
     Statement stat1 = conn.createStatement();
     ResultSet result1 = stat1.executeQuery(...);
     Statement stat2 = conn.createStatement();
     ResultSet result2 = stat2.executeQuery(...);
     Statement stat3 = conn.createStatement();
     ResultSet result3 = stat3.executeQuery(...);
     Statement stat4 = conn.createStatement();
     ResultSet result4 = stat4.executeQuery(...);) {
    // do something
} catch (SQLException e) {
    // handle exception
}

还是分开比较好:

try (Connection conn = [retrieve DB-Connection]) {
    try (Statement stat0 = conn.createStatement();
         ResultSet result0 = stat0.executeQuery(...)) {
        // do something
    }
    try (Statement stat1 = conn.createStatement();
         ResultSet result1 = stat1.executeQuery(...)) {
        // do something
    }
    try (Statement stat2 = conn.createStatement();
         ResultSet result2 = stat2.executeQuery(...)) {
        // do something
    }
    try (Statement stat3 = conn.createStatement();
         ResultSet result3 = stat3.executeQuery(...)) {
        // do something
    }
    try (Statement stat4 = conn.createStatement();
         ResultSet result4 = stat4.executeQuery(...);) {
        // do something
    }
} catch (SQLException e) {
    // handle exception
}

before java 7,如果Statements和ResultSets没有手动关闭并且连接本身不是closed/released[=37=,也可能会出现错误]

下面的例子是'bad practice'的例子,ResultSetStatementConnection都没有关闭

Connection conn = [retrieve DB-Connection];
try {
    Statement stat0 = conn.createStatement();
    ResultSet result0 = stat0.executeQuery(...);
    Statement stat1 = conn.createStatement();
    ResultSet result1 = stat1.executeQuery(...);
    Statement stat2 = conn.createStatement();
    ResultSet result2 = stat2.executeQuery(...);
    Statement stat3 = conn.createStatement();
    ResultSet result3 = stat3.executeQuery(...);
    Statement stat4 = conn.createStatement();
    ResultSet result4 = stat4.executeQuery(...);
} catch (SQLException e) {
    // handle exception
} 

假设打开游标的最大值为 20,并且有一个错误做法代码片段,应用程序可能 运行 很快进入 "ORA Cursor exceed"-Exception

在这种情况下,连接池

abandoning feature

照顾你和 closes/releases 的联系,并暗中 StatementResultSet 之间的联系。

结论

abandoning feature

只是后备。

最好确保 ResultSetStatementConnection 得到正确处理和关闭。