部署在 Tomcat 上的 DBCP 中的死锁问题

Posted

技术标签:

【中文标题】部署在 Tomcat 上的 DBCP 中的死锁问题【英文标题】:Deadlock issue in DBCP deployed on Tomcat 【发布时间】:2011-08-08 13:00:14 【问题描述】:

我在 Spring 配置中使用 DBCP 数据源(使用默认配置)来管理与数据库的连接,当客户端数量增加时,我会遇到死锁情况。

我发现我正在使用的 DBCP 1.2.1 中存在死锁问题,应该在 1.4 中解决。于是我升级到1.4,但问题依旧。

在线程转储中,有许多线程被阻塞,顶部有以下堆栈跟踪:

   java.lang.Thread.State: WAITING on org.apache.commons.pool.impl.GenericObjectPool$Latch@b6b09e
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)
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.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:200)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:350)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:261)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:101)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:160)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:631)

欢迎提出任何建议!

【问题讨论】:

我也遇到了类似的问题..你解决了这个问题吗..如果是,请指点一下.. 我也切换到 c3p0,对功能和稳定性非常满意。从那以后我就没有使用过 DBCP,所以我无法判断问题是否仍然存在。 【参考方案1】:

几年前我切换到c3p0。你可以试试。我相信你不必改变太多,这只是一个配置游戏。

有点相关的线程,Connection pooling options with JDBC: DBCP vs C3P0。好吧,实际上我把它联系起来了。

[已编辑,2012 年 10 月 19 日]

Tomcat 7 有一个不错的连接池,The Tomcat JDBC Connection Pool。

【讨论】:

切换连接池的情况下,jdbc-pool呢?与 c3p0 相比,DBCP 在基准测试中表现出更好的性能,并且 jdbc-pool 比两者都快。有这方面的经验吗? 不,我从未使用过 jdbc-pool。因此,对此无话可说。但是在阅读了这之后,这听起来很有希望。就性能而言,这个帖子people.apache.org/~fhanik/jdbc-pool/jdbc-pool.html 表明DBCP 的性能很差。 与 jdbc-pool 相比,DBCP 很差,但这个基准测试表明 c3p0 比两者都慢(但可能比你说的 DBCP 更健壮):tomcatexpert.com/blog/2010/03/22/… 如果有人阅读此问题,我已按照@Adeel Ansari 的建议切换到 c3p0,并且从那时起(大约 1.5 年前)在更高的负载下没有任何问题【参考方案2】:

您是否确保 commons-pool 版本与 dbcp 版本匹配?

另外,我没有在堆栈跟踪中看到死锁,它只是看起来您有线程等待连接释放。您同时尝试连接多少个线程?您为池等配置了多少个连接?

在调试这种情况时,查看已获得连接的线程在做什么也很有用。

【讨论】:

都是最新版本,DBCP 1.4,POOL 1.5.5。 POOL 1.5.2 中还有一个错误,显示的痕迹与我的完全相同,应该在 1.5.3 中修复。我使用的是 1.5.5。我还没有配置池选项,所以它使用默认值。正常情况下线程数一般在30到50个,但是当应用程序死锁时,Tomcat为每个请求创建一个线程,并且全部阻塞,因此达到150或200个线程。 所有线程在死锁条件后都在等待连接。没有其他线程在积极地做任何事情。所有这些最终都到了需要连接的地步,并且他们排在一个永远不会前进的队列中。 好的,您确定您实际上也将连接返回到池中吗? 当然。我在一个简单的配置中使用 Spring 方面(您可以在堆栈跟踪中看到效果)。这在低并发设置(10 或 20 个用户)中不是问题。我什至写了一个调试过滤器来计算每个线程中的打开/关闭并检查是否有任何泄漏。 我仍然很确定这种情况仅仅是由于连接没有返回到池中造成的。这不再需要任何线程来实际使用该连接。也许您可以通过调试记录问题存在时的实际池状态(活动、空闲等连接)来验证这一点?如果这表明池是空的,我将继续调试 Spring TX 关闭逻辑以及它如何应用于您正在使用的 DataSource 类型。【参考方案3】:

应用程序负载的增加正在增加对并发连接的要求。由于您的线程挂在borrowConnection() - 意味着您没有足够的ActiveConnections 可用。

在您的数据源属性中增加maxActive 并将WHEN_EXHAUSTED_BLOCK 设置为某个时间,例如600ms - 1000ms。只有经过 600 毫秒 -1000 毫秒后,您才会收到 No element available 异常。

【讨论】:

您所描述的会导致减速。我有一个明显的死锁(不再提供返回到池的可用连接),这从线程转储中很明显。没有一个线程在做任何事情,它们都被池阻塞了。在发布答案之前,您应该更仔细地阅读问题和答案。【参考方案4】:

我认为这是由于您的应用代码中没有关闭连接造成的,所以您只是用完了池中的连接。 也许您应该尝试在 DBCP 中设置“removeAbandoned”属性。 这在http://commons.apache.org/dbcp/configuration.html 中记录为

将此设置为 true 可以从编写不佳的应用程序中恢复数据库连接 无法关闭连接。

祝你好运!

【讨论】:

不,问题不在于池已用尽。我实际上创建了包装器并计算了打开/关闭对。另外,当有连接泄漏时,c3p0 也会阻塞,而且速度非常快(我已经测试过了)。线程转储清楚地表明线程彼此死锁。【参考方案5】:

我遇到了类似的问题,通过以下步骤解决了

    按顺序关闭所有数据库资源

    resultSet.close();
    statement.close();
    connection.close();
    

不同的驱动程序实现方式不同,如果底层的结果集没有关闭,一些驱动程序仍然会手动连接。

    Apache DBCP 默认值需要调整

dataSource.setDefaultAutoCommit(true); dataSource.setMaxActive(700); // make sure db server has it 800 dataSource.setRemoveAbandoned(true); dataSource.setTestOnBorrow(true); dataSource.setLogAbandoned(true); dataSource.setTestWhileIdle(true); dataSource.setTestOnReturn(true); dataSource.setRemoveAbandonedTimeout(60);

确保数据库服务器可以允许至少超过setMaxActive 中指定数量的 50 多个连接,因为 dbcp 首先提供 x 新连接,然后尝试清理超过 setMaxActive 数量的连接。 在清理 dbcp 时,会在服务器日志/控制台上显示哪些连接未关闭。

【讨论】:

以上是关于部署在 Tomcat 上的 DBCP 中的死锁问题的主要内容,如果未能解决你的问题,请参考以下文章

如何跟踪/记录 tomcat dbcp 池中的连接并检测不返回连接池的代码

使用 DBCP 配置 Tomcat

tomcat dbcp 和 mysql 连接池

Ubuntu Tomcat7 java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory

Tomcat 7 - 忽略数据库连接池参数 (DBCP)

java.lang.AbstractMethodError:在 org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.setCharacter