2012-07-12 145 views
20

我们刚刚从dbcp迁移到tomcat jdbc连接池。 我们试图在系统负载并收到以下异常:在tomcat jdbc连接池中缺少连接

java.sql.SQLException: [IA1856] Timeout: Pool empty. Unable to fetch a connection in 1 seconds, none available[size:125; busy:90; idle:0; lastwait:1000]. 
     at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:632) 
     at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:174) 
     at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:124) 
     at com.inneractive.model.mappings.BasicPersistenceEntityMapping.getConnection(BasicPersistenceEntityMapping.java:233) 
     at com.inneractive.model.mappings.BasicPersistenceEntityMapping.callWithConnection(BasicPersistenceEntityMapping.java:243) 
     at com.inneractive.model.mappings.PersistenceEntityMapping.get(PersistenceEntityMapping.java:194) 
     at com.inneractive.model.data.client.ClientUtils.GetClientByExamples(ClientUtils.java:353) 
     at com.inneractive.client.ExternalAdRingsClientStart.getClientInfoByRequestParametersOrInsert(ExternalAdRingsClientStart.java:1329) 
     at com.inneractive.client.ExternalAdRingsClientStart.newClientSession(ExternalAdRingsClientStart.java:245) 
     at com.inneractive.simpleM2M.web.SimpleM2MProtocolBean.generateCampaign(SimpleM2MProtocolBean.java:235) 
     at com.inneractive.simpleM2M.web.SimpleM2MProtocolBean.generateCampaign(SimpleM2MProtocolBean.java:219) 
     at com.inneractive.simpleM2M.web.AdsServlet.doGet(AdsServlet.java:175) 
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) 
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) 
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 
     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) 
     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) 
     at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) 
     at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:555) 
     at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 
     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) 
     at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859) 
     at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588) 
     at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:396) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
     at java.lang.Thread.run(Thread.java:662) 

声明本:

[size:125; busy:90; idle:0; lastwait:1000] 

哪里是不忙的连接? 在此之后,忙碌的人数不断下降, ,但我们仍然没有设法获得任何连接。

任何想法?

配置:

<Resource auth="Container" driverClassName="com.mysql.jdbc.Driver" 
       factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" loginTimeout="10000" 
       maxActive="35" maxIdle="35" maxWait="1000" name="jdbc/mysql" 
       password="-----" testOnBorrow="true" testOnReturn="false" type="javax.sql.DataSource" 
       url="jdbc:mysql://localhost:3306/my_db?elideSetAutoCommits=true&amp;useDynamicCharsetInfo=false&amp;rewriteBatchedStatements=true&amp;useLocalSessionState=true&amp;useLocalTransactionState=true&amp;alwaysSendSetIsolation=false&amp;cacheServerConfiguration=true&amp;noAccessToProcedureBodies=true&amp;useUnicode=true&amp;characterEncoding=UTF-8" 
       username="root" validationQuery="SELECT 1"/> 

ENV:Ubuntu的和Tomcat 6分贝 - mysql的

+0

您是否尝试增加连接池大小?如果你增加尺寸会发生什么? – dgregory 2012-07-12 11:58:48

+0

我无法增加连接数,因为我有多个服务器,而且我处于数据库的最高限制。但是,我认为这会发生在更大的数字上。 – 2012-07-12 12:44:08

+0

我们可以猜测,或者您可以发布您的配置。 – 2012-07-13 13:45:54

回答

16

考虑看看的ConnectionPool.java源你似乎打这个代码片段在borrowConnection()方法:

 //we didn't get a connection, lets see if we timed out 
     if (con == null) { 
      if ((System.currentTimeMillis() - now) >= maxWait) { 
       throw new SQLException("[" + Thread.currentThread().getName()+"] " + 
        "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait/1000) + 
        " seconds, none available["+busy.size()+" in use]."); 
      } else { 
       //no timeout, lets try again 
       continue; 
      } 
     } 

所以根据这一点,您的连接

con值上线检索:

PooledConnection con = idle.poll(); 

,如果你跟踪代码,你会看到idle是(根据您的配置,但默认情况下)FairBlockingQueue。您可以检查提示的实现。

一般来说,您总是必须关闭ResultSet,Statements和Connections,并且使用的连接应该正确地释放回池中。 如果不正确,可能会导致连接永远不会关闭=>永远不会再次可用(连接池“泄漏”)。

我建议你在池的状态上构建一些详细的日志并监视它以隔离问题。

来自Apache的防止数据库连接池泄露的一些指导原则:

removeAbandoned="true" 

废弃的数据库连接拆除和回收

removeAbandonedTimeout="60" 

集的秒数数据库连接已空闲之前,它是考虑放弃

logAbandoned="true" 

log as对放弃数据库连接资源的代码的追踪。请记住,“由于必须生成堆栈跟踪,所以”对已弃用连接的日志记录会增加每个连接借用的开销“。

我仍然认为稍微增加maxWait的值(1200,1500,1700 - 只是实验,从用户角度来看响应时间没有差异)将清除那些你仍然有问题的罕见情况。

5

“在哪里?是不是很忙的连接”

这听起来像是他们已经被删除,并且由于某种原因,您的连接池不会尝试重新连接它们。

这个添加到你连接到网址:

autoReconnect=true 

并添加此作为一个属性的资源应该引起死连接被自动重新连接。

validationQuery="SELECT 1" 

而且这应该可以让你看到的连接被丢弃:

logAbandoned="true" 

有一个关于堆栈溢出多个类似的问题。

Tomcat connection pooling,idle connections,and connection creation JDBC Connection pool not reopening Connections in tomcat

但是它也可能是你没有完全释放连接,这是他们的死亡原因。 JDBC MySql connection pooling practices to avoid exhausted connection pool

5

似乎是在游泳池中的错误,size变量增加,然后试图建立连接, 但如果创建失败......我们有size值大,在池中没有实际的连接 - 可怕:

//if we get here, see if we need to create one 
    //this is not 100% accurate since it doesn't use a shared 
    //atomic variable - a connection can become idle while we are creating 
    //a new connection 
    if (size.get() < getPoolProperties().getMaxActive()) { 
     //atomic duplicate check 
     if (size.addAndGet(1) > getPoolProperties().getMaxActive()) { 
      //if we got here, two threads passed through the first if 
      size.decrementAndGet(); 
     } else { 
      //create a connection, we're below the limit 
      return createConnection(now, con, username, password); 
     } 
    } //end if 
+2

如果创建失败,'size'在'createConnection()'中递减,所以它*似乎*正确,但我怀疑某些竞态条件会导致错误的值'size',因为我得到了'Size = 10,busy = 0,idle = 0'的'PoolExhaustedException'(这个池是空的,所以它应该尝试创建一个新的连接, *由于尺寸的大小,它不是空的)。 – Pino 2013-09-10 11:04:08