数据库连接耗尽!

发布于 2024-10-20 21:36:54 字数 8286 浏览 2 评论 0原文

我正在运行 Spring/Hibernate 连接到 MySQL 设置,使用 c3p0 作为连接池。由于某些奇怪的原因,当系统处于负载状态时(当然),它会耗尽连接。

该网站相当稳定,直到我们开始达到新的流量水平(超过一百个并发用户)。那时 DB 将崩溃(与 CPU 挂钩)。我的第一个行动是在应用程序中通过广泛的缓存和查询优化等来提高性能。

现在它只会间歇性地耗尽连接。它甚至看起来并不那么依赖于负载。更多的时间让我觉得这是一个泄漏,但我一生都无法弄清楚它会从哪里来。

    WARN [2011-03-07 17:19:42,409] [TP-Processor38] (JDBCExceptionReporter.java:100) - SQL Error: 0, SQLState: null
ERROR [2011-03-07 17:19:42,409] [TP-Processor38] (JDBCExceptionReporter.java:101) - An attempt by a client to checkout a Connection has timed out.
ERROR [2011-03-07 17:19:42,410] [TP-Processor38] (HttpHeadFilter.java:46) - There was a problem passing thru filter:/is-this-guy-crazy-or-just-a-huge-dancing-with-the-stars-fan
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.exception.GenericJDBCException: could not execute query
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:659)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:552)
        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.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)

Caused by: java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:106)
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:65)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:527)
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)

这是我的配置:

 <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource" ref="rootDataSource" />
    </bean>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="mappingLocations" value="classpath:hibernate-mapping.xml" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.connection.provider_class">net.sf.hibernate.connection.C3P0ConnectionProvider</prop>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.generate_statistics">true</prop>
                <prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
                <prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
                <prop key="hibernate.connection.zeroDateTimeBehavior">convertToNull</prop>
                <prop key="hibernate.bytecode.use_reflection_optimizer">${hibernate.bytecode.use_reflection_optimizer}</prop>
                <!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>-->
                <prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>

                <!--Actually, it seems the following property affects batch size (or explicit per relationship in the mapping)-->
                <!--<prop key="hibernate.default_batch_fetch_size">${hibernate.jdbc.batch_size}</prop>-->
            </props>
        </property>
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="rootDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="initialPoolSize" value="20" />
        <property name="maxPoolSize" value="200" />
        <property name="checkoutTimeout" value="30000" />
        <property name="maxStatements" value="180" />

        <property name="minPoolSize">
            <value>${hibernate.c3p0.minPoolSize}</value>
        </property>
        <property name="acquireRetryAttempts">
            <value>${hibernate.c3p0.acquireRetryAttempts}</value>
        </property>
        <property name="acquireIncrement">
            <value>${hibernate.c3p0.acquireIncrement}</value>
        </property>
        <property name="idleConnectionTestPeriod">
            <value>${hibernate.c3p0.idleConnectionTestPeriod}</value>
        </property>
        <property name="maxIdleTime">
            <value>${hibernate.c3p0.maxIdleTime}</value>
        </property>
        <property name="maxIdleTimeExcessConnections">
            <value>${hibernate.c3p0.maxIdleTimeExcessConnections}</value>
        </property>
        <property name="maxConnectionAge">
            <value>${hibernate.c3p0.maxConnectionAge}</value>
        </property>
        <property name="preferredTestQuery">
            <value>${hibernate.c3p0.preferredTestQuery}</value>
        </property>
        <property name="testConnectionOnCheckin">
            <value>${hibernate.c3p0.testConnectionOnCheckin}</value>
        </property>
        <property name="numHelperThreads">
            <value>${hibernate.c3p0.numHelperThreads}</value>
        </property>
        <property name="unreturnedConnectionTimeout">
            <value>${hibernate.c3p0.unreturnedConnectionTimeout}</value>
        </property>
        <property name="debugUnreturnedConnectionStackTraces">
            <value>${hibernate.c3p0.debugUnreturnedConnectionStackTraces}</value>
        </property>
        <property name="automaticTestTable">
            <value>${hibernate.c3p0.automaticTestTable}</value>
        </property>
    </bean>
    hibernate.c3p0.acquireIncrement=5
hibernate.c3p0.minPoolSize=20
hibernate.c3p0.acquireRetryAttempts=30
hibernate.c3p0.idleConnectionTestPeriod=3600
hibernate.c3p0.maxIdleTime=7200
hibernate.c3p0.maxIdleTimeExcessConnections=1800    
hibernate.c3p0.maxConnectionAge=14400
hibernate.c3p0.preferredTestQuery=select 1;
hibernate.c3p0.testConnectionOnCheckin=false
hibernate.c3p0.numHelperThreads=6
hibernate.c3p0.unreturnedConnectionTimeout=0
hibernate.c3p0.debugUnreturnedConnectionStackTraces=true
hibernate.c3p0.automaticTestTable=test_connection;

我正在运行 OpenSessionInViewInterceptor,它应该关闭连接:

 <bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
    <property name="sessionFactory">
        <ref bean="sessionFactory" />
    </property>
    <property name="flushModeName">
        <value>FLUSH_AUTO</value>
    </property>

</bean>

我还使用 @Transactional 的 spring 注释,因为我在非 Web 前端代码中重用了我的服务。

这里实际上只有两个选项,完成后不会释放连接。或者它正在闲逛,与数据库聊天,就像它试图进入她的裤子一样。 如果有人有任何想法,我将不胜感激 thx

跟进:最后发现我由于使用 OpenSessionInViewInterceptor 而泄漏了连接。我将 Spring Security 作为过滤器运行,因此它会连接到数据库并且永​​远不会关闭它们。修复方法是将 OpenSessionInViewInterceptor 移至 OpenSessionInViewFilter。

I'm running a Spring/Hibernate connecting to MySQL setup using c3p0 as my connection pool. For some bizarre reason it runs out of connections when the system is under load (of course).

The site was pretty stable until we started hitting a new level of traffic (over a hundred concurrent users). At that point the DB would melt down (peg the CPU). My first action was in the application to improve performance through extensive caching and optimization of queries etc.

Now it will just run out of connections intermittently. It doesn't even seem that dependent on load. More on time which makes me think it's a leak but for the life of me I can't figure out where it would be coming from.

    WARN [2011-03-07 17:19:42,409] [TP-Processor38] (JDBCExceptionReporter.java:100) - SQL Error: 0, SQLState: null
ERROR [2011-03-07 17:19:42,409] [TP-Processor38] (JDBCExceptionReporter.java:101) - An attempt by a client to checkout a Connection has timed out.
ERROR [2011-03-07 17:19:42,410] [TP-Processor38] (HttpHeadFilter.java:46) - There was a problem passing thru filter:/is-this-guy-crazy-or-just-a-huge-dancing-with-the-stars-fan
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.exception.GenericJDBCException: could not execute query
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:659)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:552)
        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.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)

Caused by: java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:106)
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:65)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:527)
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)

Here's my configuration:

 <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource" ref="rootDataSource" />
    </bean>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="mappingLocations" value="classpath:hibernate-mapping.xml" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.connection.provider_class">net.sf.hibernate.connection.C3P0ConnectionProvider</prop>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.generate_statistics">true</prop>
                <prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
                <prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
                <prop key="hibernate.connection.zeroDateTimeBehavior">convertToNull</prop>
                <prop key="hibernate.bytecode.use_reflection_optimizer">${hibernate.bytecode.use_reflection_optimizer}</prop>
                <!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>-->
                <prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>

                <!--Actually, it seems the following property affects batch size (or explicit per relationship in the mapping)-->
                <!--<prop key="hibernate.default_batch_fetch_size">${hibernate.jdbc.batch_size}</prop>-->
            </props>
        </property>
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="rootDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="initialPoolSize" value="20" />
        <property name="maxPoolSize" value="200" />
        <property name="checkoutTimeout" value="30000" />
        <property name="maxStatements" value="180" />

        <property name="minPoolSize">
            <value>${hibernate.c3p0.minPoolSize}</value>
        </property>
        <property name="acquireRetryAttempts">
            <value>${hibernate.c3p0.acquireRetryAttempts}</value>
        </property>
        <property name="acquireIncrement">
            <value>${hibernate.c3p0.acquireIncrement}</value>
        </property>
        <property name="idleConnectionTestPeriod">
            <value>${hibernate.c3p0.idleConnectionTestPeriod}</value>
        </property>
        <property name="maxIdleTime">
            <value>${hibernate.c3p0.maxIdleTime}</value>
        </property>
        <property name="maxIdleTimeExcessConnections">
            <value>${hibernate.c3p0.maxIdleTimeExcessConnections}</value>
        </property>
        <property name="maxConnectionAge">
            <value>${hibernate.c3p0.maxConnectionAge}</value>
        </property>
        <property name="preferredTestQuery">
            <value>${hibernate.c3p0.preferredTestQuery}</value>
        </property>
        <property name="testConnectionOnCheckin">
            <value>${hibernate.c3p0.testConnectionOnCheckin}</value>
        </property>
        <property name="numHelperThreads">
            <value>${hibernate.c3p0.numHelperThreads}</value>
        </property>
        <property name="unreturnedConnectionTimeout">
            <value>${hibernate.c3p0.unreturnedConnectionTimeout}</value>
        </property>
        <property name="debugUnreturnedConnectionStackTraces">
            <value>${hibernate.c3p0.debugUnreturnedConnectionStackTraces}</value>
        </property>
        <property name="automaticTestTable">
            <value>${hibernate.c3p0.automaticTestTable}</value>
        </property>
    </bean>
    hibernate.c3p0.acquireIncrement=5
hibernate.c3p0.minPoolSize=20
hibernate.c3p0.acquireRetryAttempts=30
hibernate.c3p0.idleConnectionTestPeriod=3600
hibernate.c3p0.maxIdleTime=7200
hibernate.c3p0.maxIdleTimeExcessConnections=1800    
hibernate.c3p0.maxConnectionAge=14400
hibernate.c3p0.preferredTestQuery=select 1;
hibernate.c3p0.testConnectionOnCheckin=false
hibernate.c3p0.numHelperThreads=6
hibernate.c3p0.unreturnedConnectionTimeout=0
hibernate.c3p0.debugUnreturnedConnectionStackTraces=true
hibernate.c3p0.automaticTestTable=test_connection;

I am running OpenSessionInViewInterceptor which should be closing the connections:

 <bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
    <property name="sessionFactory">
        <ref bean="sessionFactory" />
    </property>
    <property name="flushModeName">
        <value>FLUSH_AUTO</value>
    </property>

</bean>

I am also using the spring annotations for @Transactional since I reuse my services in non web fronted code.

There are really only two options here, it's not releasing connections when done. Or it's hanging out chatting up the db like it's trying to get in her pants.
If anyone has any ideas I would be grateful
thx

FOLLOW UP: In the end it turns out I was leaking connections due to using OpenSessionInViewInterceptor. I had spring security running as a filter so it would connect to the DB and never close them. The fix was to move the OpenSessionInViewInterceptor to OpenSessionInViewFilter.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

清醇 2024-10-27 21:36:54

尝试启用日志记录并将 c3p0.debugUnreturnedConnectionStackTraces 属性设置为 true。还将 c3p0.unreturnedConnectionTimeout 设置为小于平均查询时间(1 秒?)。然后任何花费超过超时时间的事情都会记录堆栈跟踪。这应该可以让你很快缩小范围。

如果堆栈跟踪没有任何模式,则可能只是您的池太小了。您说的是 100 个并发用户,但您知道每秒有多少个查询吗?如果每秒 100 个查询并且您有 20 个连接,则每个 sql 执行需要花费不到 200 毫秒(20 个连接 => 每秒 20 秒的工作时间来执行 100 个查询)。

Try enabling logging and setting the c3p0.debugUnreturnedConnectionStackTraces property to true. Also set c3p0.unreturnedConnectionTimeout to something smaller than your average query time (1 sec?). Then any thing that takes longer than the timeout will log a stack trace. This should allow you to narrow down things pretty quickly.

If there's no pattern to the stack traces, it could just be that your pool is too small. You said 100 concurrent users, but any idea how many queries per second this is? If it's 100 queries per second and you have 20 connections, then each sql execution needs to take less than 200 ms (20 connections => 20 total seconds of work per sec of wall clock time to do 100 queries).

浴红衣 2024-10-27 21:36:54

@Transactional 泄漏连接的可能性很小 - 否则,您的网站将在前 100 个请求后停止工作。

但发生这种情况还有另一个原因:

也许您为“死”连接设置了超时,并且某些查询花费的时间比该超时长。这意味着您的池将一个繁忙的连接作为“死”连接从池中删除,并向数据库请求另一个连接 - 直到数据库拔掉插头。

要对此进行调试,请为连接池启用日志记录,以便您可以看到它何时请求新连接。

It's pretty unlikely that @Transactional leaks connections - otherwise, your site would stop working after the first 100 requests.

But there is another reason why this happens:

Maybe you have set a timeout for "dead" connections and some queries take longer than that. That means that your pool removed a busy connection as "dead" from the pool and requests another from the DB - until the DB pulls the plug.

To debug this, enable logging for your connection pool, so you can see when it requests new connections.

七颜 2024-10-27 21:36:54

无论您对 C3P0 的配置如何(通过休眠),您都可能受到 MySQL 本身施加的限制。请记住,默认情况下 MySQL 允许的最大连接数是 100!因此,即使您告诉 C3P0 池化最多 200、500 或 1000 个连接,这也是无法实现的。使用以下命令打开 MySQL shell:

$ msql -u [user] -p

并键入以下内容以获得允许的最大连接数:

$ show variables where Variable_name='max_connections';

如果返回的数字对于您的应用程序来说太低,请考虑更改它(编辑 my.cnf 文件,通常位于 /etc/mysql/ 内) Linux 系统)。

Regardless of the configuration you have for C3P0 (through hibernate) you might have a restriction imposed by MySQL itself. Keep in mind that by default the maximum number of connections allowed by MySQL is 100! So even if you tell C3P0 to pool up to 200, 500 or 1000 connections, this will be unachievable. Open a MySQL shell using:

$ msql -u [user] -p

And type the following to get the maximum number of connections allowed:

$ show variables where Variable_name='max_connections';

If the number returned is too low for your application, consider changing it (edit your my.cnf file, usually located inside /etc/mysql/ on Linux systems).

爱要勇敢去追 2024-10-27 21:36:54

我也遇到了这个问题。原因是用户没有主机的授权,因为 /etc/hosts 条目已被修改。

I also had this problem. The cause was that the user does not has the grants for the host, because the /etc/hosts entry has been modified.

橘味果▽酱 2024-10-27 21:36:54

我也遇到了这个问题,我通过将 C3P0 的属性 checkoutTimeout 设置为 0 而不是大于 0 的值来解决它。

事实上,我有很多线程正在等待连接,10 秒后,同样的情况发生你的错误。

请参阅此处的文档: http://www.mchange.com/projects/c3p0/#checkoutTimeout< /a>

I also had this problem and I solved it by setting the property checkoutTimeout of C3P0 to 0 instead of a value higher than 0.

In fact I had lots of threads waiting for a connection and after 10s, the same erros as yours occured.

See the doc here : http://www.mchange.com/projects/c3p0/#checkoutTimeout

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文