Кончаются соединения с БД! - PullRequest
10 голосов
/ 08 марта 2011

Я использую подключение Spring / Hibernate к настройке MySQL, используя c3p0 в качестве пула соединений. По какой-то странной причине у него кончаются соединения, когда система находится под нагрузкой (конечно).

Сайт был довольно стабильным, пока мы не начали выходить на новый уровень трафика (более ста одновременно работающих пользователей). В этот момент БД будет таять (привязать процессор). Моим первым действием было приложение для повышения производительности за счет обширного кэширования и оптимизации запросов и т. Д.

Теперь у него просто будут прерываться соединения. Это даже не кажется, что зависит от нагрузки. Больше времени, и это заставляет меня думать, что это утечка, но я не могу понять, откуда она взялась.

    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, поскольку я повторно использую свои сервисы в не веб-ориентированном коде.

Здесь на самом деле есть только два варианта, это не освобождает соединения, когда сделано. Или он болтается в чате с БД, как будто пытается попасть в ее штаны. Если у кого-то есть идеи, буду благодарен ТНХ

СЛЕДУЙТЕ ЗА: В конце концов выясняется, что у меня были утечки соединений из-за использования OpenSessionInViewInterceptor. У меня была Spring Security, работающая в качестве фильтра, чтобы он подключался к БД и никогда не закрывал их. Исправление состояло в том, чтобы переместить OpenSessionInViewInterceptor в OpenSessionInViewFilter.

Ответы [ 5 ]

12 голосов
/ 08 марта 2011

Попробуйте включить ведение журнала и установить для свойства c3p0.debugUnreturnedConnectionStackTraces значение true.Также установите c3p0.unreturnedConnectionTimeout на что-то меньшее, чем ваше среднее время запроса (1 сек?).Тогда любая вещь, которая занимает больше времени, чем тайм-аут, записывает трассировку стека.Это должно позволить вам довольно быстро сузить круг.

Если в трассировке стека нет паттернов, возможно, ваш пул слишком мал.Вы сказали, что 100 одновременных пользователей, но есть идеи, сколько запросов в секунду это?Если это 100 запросов в секунду, и у вас есть 20 подключений, то на каждое выполнение sql требуется менее 200 мс (20 подключений => 20 общих секунд работы в секунду времени настенных часов для выполнения 100 запросов).

5 голосов
/ 08 марта 2011

Маловероятно, что @Transactional утечка соединений - иначе ваш сайт перестал бы работать после первых 100 запросов.

Но есть и другая причина, почему это происходит:

Возможно, у вас естьустановить тайм-аут для «мертвых» соединений, а некоторые запросы занимают больше времени.Это означает, что ваш пул удалил занятое соединение как «мертвое» из пула и запросил другое у БД - пока БД не вытянет штекер.

Чтобы отладить это, включите ведение журнала для вашего пула соединений, чтобы вы моглипосмотрите, когда он запрашивает новые подключения.

3 голосов
/ 18 марта 2011

Независимо от конфигурации, которую вы используете для C3P0 (через hibernate), у вас может быть ограничение, наложенное самим MySQL. Имейте в виду, что по умолчанию максимальное количество соединений, разрешенных MySQL, составляет 100! Поэтому, даже если вы скажете C3P0 о пуле до 200, 500 или 1000 соединений, это будет недостижимо. Откройте оболочку MySQL, используя:

$ msql -u [user] -p

И введите следующее, чтобы получить максимально допустимое количество подключений:

$ show variables where Variable_name='max_connections';

Если возвращаемое число слишком мало для вашего приложения, рассмотрите возможность его изменения (отредактируйте файл my.cnf, обычно расположенный в / etc / mysql / в системах Linux).

0 голосов
/ 12 июля 2013

У меня также была эта проблема, и я решил ее, установив для свойства checkoutTimeout C3P0 значение 0 вместо значения выше 0.

Фактически у меня было много потоков, ожидающихСоединение и через 10 секунд появляются те же ошибки, что и у вас.

См. документ здесь: http://www.mchange.com/projects/c3p0/#checkoutTimeout

0 голосов
/ 14 ноября 2011

У меня тоже была эта проблема. Причина заключалась в том, что у пользователя нет прав доступа к хосту, поскольку запись в / etc / hosts была изменена.

...