tomcat jdbc 连接池中缺少连接

Posted

技术标签:

【中文标题】tomcat jdbc 连接池中缺少连接【英文标题】:Missing connections in tomcat jdbc connection pool 【发布时间】:2012-07-12 03:41:43 【问题描述】:

我们刚刚从 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

【问题讨论】:

您是否尝试增加连接池大小?如果增加尺寸会怎样? 我无法增加连接数,因为我有多个服务器,而且我处于数据库的上限。但是,我认为这将发生在更大的数量上.. 我们可以猜测,或者您可以发布您的配置。 @ChristopherSchultz 这听起来像是 JDBC/Tomcat 错误,配置中的什么可能导致这样的事情? 也许它没有释放回池的连接。尝试监控 mysql 端的连接,使用链接:devdaily.com/blog/post/mysql/… 还将你的 maxWait 增加到至少 10000(默认值是 30000,可能是有原因的) 【参考方案1】:

似乎是池中的一个错误,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

【讨论】:

如果创建失败,sizecreateConnection() 中递减,所以它似乎正确,但是我怀疑某些竞争条件会导致 size 的错误值因为我得到了PoolExhaustedExceptionsize=10, busy=0, idle=0(池是空的,所以它应该尝试创建一个新连接而不是等待一个空闲的连接,但它认为它不是空的,因为大小值)。【参考方案2】:

看一下ConnectionPool.java的来源,你似乎在borrowConnection()方法中打了这段代码sn-p:

        //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。您可以查看实现以获取提示。

一般来说,您总是必须关闭结果集、语句和连接,并且应该将使用过的连接正确地释放回池中。 未正确执行此操作可能会导致连接永远不会关闭 => 永远无法再次使用(连接池“泄漏”)。

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

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

removeAbandoned="true"

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

removeAbandonedTimeout="60"

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

logAbandoned="true"

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

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

【讨论】:

【参考方案3】:

“不忙的连接在哪里?”

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

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

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

【讨论】:

以上是关于tomcat jdbc 连接池中缺少连接的主要内容,如果未能解决你的问题,请参考以下文章

我可以将锁定模式设置为在 Informix - JDBC - tomcat 连接池中等待吗?

Tomcat JDBC连接池:testOnBorrow vs testWhileIdle

如何记录 Tomcat 7 JDBC 连接池、连接创建

Tomcat jdbc 连接池在热的变化

JDBC连接池的testQuery/validationQuery设置

连接池中没有合适的驱动程序异常