【问题标题】:Spring ORM with C3P0 or DBCP is leaking connections带有 C3P0 或 DBCP 的 Spring ORM 正在泄漏连接
【发布时间】:2011-12-11 02:07:41
【问题描述】:

在 DBCP 和 C3P0 的最新版本上,使用 Spring 的 Ibatis 支持,我遇到了两个泄漏连接的问题。

场景是有一个日志运行SQL,它锁定了多个表。当用户触发命中锁定表的查询时,这会导致我的池中的连接最大化。最后,管理员进入 MySQL 并在长时间运行的 SQL 上执行kill query <id>

如果有足够多的线程(在我的情况下大约 50 个或更多)正在等待将 DB 线程检入池中,那么我会在线程转储中看到类似以下内容:

 java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1315)
    at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:557)
    - locked <0x00002aaacbb01118> (a com.mchange.v2.resourcepool.BasicResourcePool)
    at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:477)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525)
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:113)
    at 

  java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:485)
    at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1104)
    - locked <0x00002aab0f030620> (a org.apache.commons.pool.impl.GenericObjectPool$Latch)
    at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:113)
    at 

这些线程永远等待。

如果池已满,并且只有少数(大约 5 个)线程正在等待池中的空闲连接,则不会发生这种情况。

我知道有可以解决这个问题的配置(设置超时等),但我很感兴趣为什么会发生这种情况?当有 50 个或更多线程在等待连接并且我终止了长时间运行的 SQL 时,为什么没有将活动线程返回到池中?

更新:我应该明确表示我使用的是 Spring 3.0.2 和 伊巴蒂斯 2.3。我使用管理我的 SqlMapClientTemplate 为我连接。在这一点上,我开始认为它是 Ibatis 2.3 无法正确处理重负载。

【问题讨论】:

  • 执行查询后是否关闭连接?
  • @BalusC 我在 Spring 中使用 ComboPooledDataSource 或 BasicDataSource,因此我不管理在我的 Java 代码中打开 c 关闭这些连接。也许春天是问题所在。
  • 你应该展示一些你如何使用连接的代码。仅仅因为您使用 Spring 来管理数据源,并不意味着它会为您关闭连接。
  • @Nick 实际上确实如此,只需阅读源代码即可。您将 DataSource 传递给 SqlMapClientTemplate,它管理获取和关闭您的连接。
  • 抱歉,不太清楚您使用的是SqlMapClientTemplate

标签: java mysql database spring c3p0


【解决方案1】:

就像@BalusC 所问的一样,你要关闭你的连接吗?它们应该在 Java try-catch-finallyfinally 子句中关闭。将每个 connection.close() 方法包装在自己的 try { con.close() } catch (Exception ignore) {}

您可以选择为ResultSet 然后Statement 然后Connection 这样做。在初始 try 块之外将 ConnectionStatementResultSet 声明为 null 并在 try 块中实例化。

Connection con = null;
Statement stmt = null;
ResultSet rs = null;

try {

    con = getConnectionFromPoolMethod();

    // ...
    // instantiate your statement and result set as normal
    // make your sql call; 
    // extract data from result set to appropriate POJO

} catch (Exception ex) {
    // handle your exception, log, wrap, enhance or rethrow or whatever
} finally {
    if (rs != null) try { rs.close(); } catch (Exception ignore) {}
    if (stmt != null) try { stmt.close(); } catch (Exception ignore) {}
    if (con != null) try { con.close(); } catch (Exception ignore) {}
}

如果您愿意,而不是在包装 SQL 调用的每个 finally 中的所有上述代码,您可以选择使用 DbUtils 实用程序类 Apache Commons DbUtils

import org.apache.commons.dbutils.DbUtils;

并应用closeQuietly 方法。 finally 块将如下所示:

} finally {
    DbUtils.closeQuietly(con, stmt, rs);
}

【讨论】:

  • +1 没问题 - 很好的答案。并不是要吹毛求疵,但捕获特定的已检查异常(例如 SQLException)被认为比捕获普通的 Exception 更好。
  • 这是更好的做法,但如果他像我猜的那样是新手,他可能不会考虑导入另一个类。暂时编辑以添加使用 DbUtils 的选项。
  • @FredCheese 我的 Java 代码没有管理这些连接。我创建数据源,然后将其传递给 SqlMapClientTemplate,它是一个 Spring 对象。因此,我永远不必管理关闭连接。
  • @FredCheese 不幸的是,我处于无法控制系统上运行的 SQL 的情况,因此总是有可能陷入这种类型的死锁。我可以要求我的池化库删除废弃的连接,但到目前为止这是最好的代码修复。
  • 不管怎样,了解长时间运行的调用(SelectInsertUpdateDelete)正在执行哪种类型的 SQL 调用可能有助于为您提供一个答案。您可能需要在数据库人员和 SQL 调用生成方面留出一些余地才能修复此问题。
【解决方案2】:

只是添加到@BalusC 的评论和@JoshDM's answer,当您在 Java 代码中对 Connection 调用 close() 时,在幕后这实际上只是将该连接检查回您的连接池,而不是物理关闭它。这就是为什么始终通过 Java 代码关闭连接很重要,无论底层 JDBC 驱动程序是否正在池化它们。

更多关于这个的讨论在this post

【讨论】:

    【解决方案3】:

    在使用 c3po 时,我强烈建议您试试这个feature。当您在事务之外执行数据库修改时,通常会出现连接泄漏。带有未提交写入的连接不能被重用,并且会从池中丢失。如果您启用此调试并具有相当长的超时时间,您将能够看到带有可疑数据库操作的堆栈跟踪。

    确保堆栈跟踪中看到的操作正确管理事务。还可以通过将 c3p0 记录器设置为调试级别来监控连接池的使用情况。

    【讨论】:

    • 谢谢,DBCP 也有一个类似的配置,叫做 removeAbandoned。当我设置removedAbandoned 时,我发现我正在失去连接。我仍然不知道为什么应用程序会丢失这些连接。
    • 我对 dbcp 不熟悉,但我已经通过 c3p0 debugUnreturnedConnectionStackTraces 追踪到不少连接泄漏。请记住,您将看到的堆栈跟踪是在从池中获取连接时生成的。这意味着您将看到导致连接丢失的确切呼叫。然后确保上面有@Transactional 或等效的 sql 调用来改变数据库。
    • 啊,谢谢,c3p0 可能比 dbcp 更冗长。遗憾的是,这部分代码不支持事务。
    • 我想你应该在自动提交模式下工作,如果是这样的话,我描述的情况不应该发生。最好确认这一点并分析来自 c3p0 的堆栈跟踪。
    猜你喜欢
    • 1970-01-01
    • 2011-02-18
    • 2011-02-07
    • 2021-03-12
    • 2014-08-03
    • 2012-07-06
    • 2019-12-04
    • 2015-12-04
    • 1970-01-01
    相关资源
    最近更新 更多