tomcat连接池没有关闭连接?

时间:2018-04-18 08:07:57

标签: tomcat connection pool

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

除了解决这些问题外,我们还想通过设置:

来发布空闲会话
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;

这显示了“旧”会话/游标和语句,绝对超过1分钟。

我错过了其他选择吗? 谢谢和欢呼,E。

1 个答案:

答案 0 :(得分:2)

这是一个非常棘手的话题

<强>前言

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

建立与数据库的物理连接需要时间。通过保持连接打开(在池中)来“保存”这个时间。

<强>连接处理

在java 7之前,您必须确保在使用它之后关闭连接。主要是在最后一块:

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

结合连接池,连接没有物理关闭,在StatementResultSet关闭后,它刚刚释放到连接池,可以重复使用。

从java 7开始,上面的代码应该/看起来像那样:

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

这是新的“资源试用”功能。只要保留try-block,try的parantheses中的资源就会被关闭(autocloseable)。这些parantheses可以包含严格的,以分号分隔的自动封闭资源。

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
}

如果连接未手动关闭/释放或通过“try-catch with resources”处理,则连接池具有可配置的后备功能 -

放弃功能
处理未关闭/未释放(已放弃)的连接。

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

  

默认情况下禁用放弃功能,可以使用配置   以下属性:

     
      
  • removeAbandoned - true或false:是否删除废弃的   从游泳池连接。默认值:false
  •   
  • removeAbandonedTimeout - The   假定借用连接的秒数   弃。默认值:300
  •   
  • logAbandoned - true或false:是否记录   应用程序代码的堆栈跟踪放弃了语句或   连接。这增加了严重的开销。默认值:false
  •   

游标和“ORA游标超过”-Exception

打开游标的最大值是变量数据库属性。

因此,通过在一个连接中执行大量查询而不关闭“先前”打开/创建的结果集和语句,可能会发生ORA Cursor exceed异常

以下示例导致五个打开的游标。在这种情况下,使用带有资源的try-catch,并通过离开try-block自动关闭ResultSet s,StatementConnection

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
}

在java 7之前,如果StatementResultSet未手动关闭且连接本身未关闭/已释放,则也可能发生错误

以下示例是一个“不良做法”示例,ResultSetStatement以及Connection都未关闭

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,并且有一个bad-practice-code-snippet,应用程序可以非常快速地运行到“ORA Cursor exceeded”-Exception

在这种情况下,连接池

  

放弃功能

照顾你并关闭/释放连接,并隐含StatementResultSet内的连接。

<强>结论

  

放弃功能

只是一个后备。

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