Tomcat JDBC连接池中的连接缺失。

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。数据库 - mysql


你尝试过增加连接池大小吗?如果增加大小会发生什么? - dgregory
我们可以猜测,或者您可以发布您的配置。 - Christopher Schultz
@ChristopherSchultz 听起来像是JDBC/Tomcat的一个bug,配置上有什么可能导致这样的问题? - Nir Alfasi
@Andi - 不确定,数据库似乎没问题...而且那里面真的没有太多数据。我甚至不知道哪个版本有修复,所以我能试试看。你知道吗? - yael alfasi
3
也许它没有释放回连接池。尝试监视MySQL端上的连接,使用此链接:http://www.devdaily.com/blog/post/mysql/how-show-open-database-connections-mysql 同时将maxWait增加至少到10000(默认值为30000,可能有原因)。 - hovanessyan
显示剩余11条评论
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;
            }
        }

根据此,您的连接是

con的值在以下行中检索:

PooledConnection con = idle.poll();

如果您追踪代码,您会发现idle是(取决于您的配置,但默认情况下为)FairBlockingQueue。您可以检查实现以获取提示。

通常,您必须关闭ResultSets、Statements和Connections,并且使用过的连接应正确释放回池中。不正确地执行此操作可能导致连接永远不会被关闭 => 永远无法重新使用(连接池“泄漏”)。

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

Apache提供了一些预防数据库连接池泄漏的指南:

removeAbandoned="true"

废弃的数据库连接将被移除并回收利用。

removeAbandonedTimeout="60"

设置数据库连接空闲的秒数,超过这个时间就被视为废弃。

logAbandoned="true"

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

我仍然认为略微增加maxWait值(1200、1500、1700-只需进行实验,用户响应时间不会有任何差异)将清除那些罕见的情况,在这些情况中您仍然遇到问题。


6

连接池似乎存在一个bug,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
如果在createConnection()中创建失败,则会减少size,因此它看起来是正确的,但我怀疑某些竞争条件导致了size的错误值,因为我得到了PoolExhaustedException,其中size=10,busy=0,idle=0(池为空,所以它应该尝试创建一个新连接而不是等待空闲连接,但由于size的值,它认为它不是空的)。 - Pino

6

"有哪些未忙碌的连接?"

听起来像是它们被放弃了,因为某种原因你的连接池没有尝试重新连接它们。

将以下内容添加到你连接的URL中:

autoReconnect=true

将此添加为资源的属性应该会自动重新连接死亡连接。
validationQuery="SELECT 1"

此外,这应该允许您看到连接被断开:
logAbandoned="true"

在stackoverflow上有多个类似的问题。

Tomcat连接池,空闲连接和连接创建 JDBC连接池不能在Tomcat中重新打开连接

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接