24

我们刚刚从 dbcp 迁移到 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"/>

环境:ubuntu 和 tomcat 6. db - mysql

4

3 回答 3

18

查看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;
            }
        }

所以根据这个,你的连接是Null

的值con在以下行检索:

PooledConnection con = idle.poll();

如果您跟踪代码,您将看到idle(取决于您的配置,但默认情况下)FairBlockingQueue。您可以检查实现以获取提示。

通常,您总是必须关闭 ResultSets、Statement 和 Connections,并且应该将使用过的连接正确地释放回池中。不正确地这样做可能会导致连接永远不会被关闭=>永远不能再被重用(连接池“泄漏”)。

我建议您对池的状态构建一些详细的日志记录并对其进行监视以隔离问题。

Apache 的一些防止数据库连接池泄漏的指南:

removeAbandoned="true"

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

removeAbandonedTimeout="60"

设置数据库连接在被视为放弃之前空闲的秒数

logAbandoned="true"

记录放弃数据库连接资源的代码的堆栈跟踪。请记住,“记录废弃的连接会增加每次连接借用的开销,因为必须生成堆栈跟踪。”

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

于 2012-07-26T14:30:57.300 回答
6

“不忙的连接在哪里?”

听起来它们已被删除,并且由于某种原因您的连接池没有尝试重新连接它们。

将此添加到您要连接的 URL:

autoReconnect=true

并将其作为属性添加到资源应该会导致死连接自动重新连接。

validationQuery="SELECT 1"

此外,这应该允许您看到连接被丢弃:

logAbandoned="true"

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

Tomcat 连接池、空闲连接和连接创建 JDBC 连接池未重新打开 Tomcat 中的连接

但是,也可能是您没有完全释放连接,这是导致它们死亡的原因。 JDBC MySql 连接池实践避免连接池耗尽

于 2012-07-26T12:41:47.530 回答
6

似乎是池中的一个错误,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
于 2012-10-24T12:45:25.800 回答