使用hibernate和c3p0中断线程时的大延迟

时间:2014-12-02 19:22:56

标签: mysql hibernate c3p0

我在日志中看到了这些警告,当我这样做时,我发现数据库中的事务延迟超过正常。我在网上搜索了更多信息。

2014-12-01 18:14:03,866 [BoundedFuturePool-2] WARN  com.mchange.v2.resourcepool.BasicResourcePool  - {} com.mchange.v2.resourcepool.BasicResourcePool@2f18b0cb -- an attempt to checkout a resource was interrupted, and the pool is still live: some other thread must have either interrupted the Thread attempting checkout!
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1414)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:606)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:526)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:755)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:682)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:81)
at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446)
at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
at org.hibernate.jdbc.BorrowedConnectionProxy.invoke(BorrowedConnectionProxy.java:74)
at com.sun.proxy.$Proxy40.setReadOnly(Unknown Source)
at org.springframework.jdbc.datasource.DataSourceUtils.prepareConnectionForTransaction(DataSourceUtils.java:155)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:508)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:329)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at com.twitter.amplify.videoediting.data.TranscodingDao$$EnhancerByCGLIB$$696749fa.getTranscodeInfo(<generated>)
at com.twitter.amplify.videoediting.GetTranscodeInfoHandler$$anonfun$7.apply(GetTranscodeInfoHandler.scala:78)
at com.twitter.amplify.videoediting.GetTranscodeInfoHandler$$anonfun$7.apply(GetTranscodeInfoHandler.scala:78)
at com.twitter.util.Try$.apply(Try.scala:13)
at com.twitter.util.ExecutorServiceFuturePool$$anon$2.run(FuturePool.scala:112)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at com.twitter.amplify.util.LocalExecutorService$1.call(LocalExecutorService.java:36)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)

以下是我们的配置对象。

Using DataSource [com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 5, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2u120u951sgpypjpvvovo|73f2f11, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2u120u951sgpypjpvvovo|73f2f11, idleConnectionTestPeriod -> 0, initialPoolSize -> 0, jdbcUrl -> jdbc:mysql://db-hat-rw-master-001.global-db.twttr.net/amplify?defaultFetchSize=200, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 60, maxIdleTimeExcessConnections -> 60, maxPoolSize -> 200, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 0, numHelperThreads -> 3, preferredTestQuery -> select 1, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> true, unreturnedConnectionTimeout -> 300, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]]

感谢您的帮助! 最大

2 个答案:

答案 0 :(得分:1)

所以,警告告诉你,就像c3p0直接知道的那样:一个客户端线程试图检查一个连接 - 并等待()获取一个 - 在它上面调用了interrupt(),迫使它线程从wait()中断。被阻止的线程是一个应用程序线程(它是一个scala应用程序,在com.twitter.amplify.videoediting.GetTranscodeInfoHandler中调用一些东西)。最有可能的是,应用程序的某些其他部分会监视这些线程,如果它们挂起则会中断它们。如果是这样,那么您所观察到的延迟就是核心问题。您的连接池被最大化,因为它已经命中maxPoolSize或者它已经没有,但需要从DBMS获取更多连接。线程被卡住等待(等待连接),然后它们等待太长时间并被外部监视器杀死。

你的maxPoolSize 200相当大,但显然必须根据需求来判断。也许你有200多个同步客户端线程。 (顺便说一句,您可以通过JMX监控这些内容,直接看到您的游泳池是否达到了它的上限。)

但我的猜测(并且只是那个)是你的问题与未能快速扩展池有关。对于正在运行的规模的应用程序,c3p0的默认线程池大小(由名为numHelperThreads的配置参数古怪地描述,对不起)在3处太小。首先我和#39 ; d do是最多10或20的斜坡。如果这不能解决你的问题,你可以查看JMX,看看你是否真的碰到了你的数据源maxPoolSize的200 (如果你是,你也可以提出这个问题,虽然最终会因为线程争用而导致收益递减。)你也可以监视numThreadsAwaitingCheckoutDefaultUser,这也应该是JMX提供的。

但我从numHelperThreads开始。它的价值太小也可能是问题所在。

答案 1 :(得分:1)

当线程进入BasicResourcePool.awaitAvailable并在第1414行休眠时,此异常将始终发生,而另一个线程在断开== false时进入BasicResourcePool.close(boolean)第1225行。

在这种情况下,close内部的线程将在等待线程上调用Thread.interrupt()。

请参阅此代码第1276行。正在中断睡在BasicResourcePool.awaitAvailable第1441行的线程。

        for (Iterator ii = acquireWaiters.iterator(); ii.hasNext(); )
            ((Thread) ii.next()).interrupt();

值得注意的是,AwaitAvailable被称为prelimCheckoutResource - 第568行,它是同步的。因此,当它在第1414行睡觉时,它释放锁定,这允许另一个线程进入关闭,这也是同步的。

因此,一旦在关闭内调用Thread.interrupt,在BasicResourcePool.close()中的中断线程从synchronized块返回并释放锁之后,所有休眠线程都不能实际唤醒,因为从.wait返回( )要求线程重新获取锁。

即。一旦另一个线程进入同步块,一旦超时到期,你的休眠线程就不会被唤醒,或者确实处理InterruptedException,直到另一个线程退出synchronized块。

因此,当一个从循环调用close的同步方法存在 if 时,你的线程将无法返回,直到其他线程释放锁。

值得一提的是,BasicResourcePool.connectionErrorOccurred()在循环中调用synchronized方法BasicResourcePool.resetPool()和此方法,调用close,这会中断你的线程,但阻止它被唤醒直到resetPool释放锁之后。 / p>

只是推测这种情况正在发生,但如果确实如此,则会导致您看到的异常,也可能会解释延迟。