web-dev-qa-db-ja.com

DB接続が不足しています!

接続プールとしてc3p0を使用してMySQLセットアップに接続するSpring/Hibernateを実行しています。いくつかの奇妙な理由で、システムに負荷がかかっているときに接続が不足します(もちろん)。

新しいレベルのトラフィック(100人以上の同時ユーザー)に到達し始めるまで、サイトはかなり安定していました。その時点で、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>

また、Webフロント以外のコードでサービスを再利用しているため、@ TransactionalのSpringアノテーションも使用しています。

ここには実際には2つのオプションしかありません。完了しても、接続は解放されません。または、ズボンに乗り込もうとしているように、データベースとチャットしている。誰かが何かアイデアを持っているなら、私は感謝しますthx

ファローアップ: 結局、OpenSessionInViewInterceptorを使用したために接続がリークしていたことが判明しました。私はSpringSecurityをフィルターとして実行していたので、DBに接続し、決して閉じませんでした。修正は、OpenSessionInViewInterceptorをOpenSessionInViewFilterに移動することでした。

10
matsientst

@Transactional接続のリーク-そうしないと、最初の100回のリクエスト後にサイトが機能しなくなります。

しかし、これが発生する別の理由があります。

「デッド」接続のタイムアウトを設定していて、クエリによってはそれよりも時間がかかる場合があります。これは、DBがプラグを抜くまで、プールがビジー接続を「デッド」としてプールから削除し、DBから別の接続を要求することを意味します。

これをデバッグするには、接続プールのログを有効にして、新しい接続がいつ要求されるかを確認できるようにします。

5
Aaron Digulla

ロギングを有効にして、c3p0.debugUnreturnedConnectionStackTracesプロパティをtrueに設定してみてください。また、c3p0.unreturnedConnectionTimeoutを平均クエリ時間(1秒?)よりも短い値に設定します。次に、タイムアウトよりも時間がかかるものは、スタックトレースをログに記録します。これにより、物事をかなりすばやく絞り込むことができます。

スタックトレースにパターンがない場合は、プールが小さすぎる可能性があります。 100人の同時ユーザーと言いましたが、これが1秒あたりのクエリ数について何か考えはありますか? 1秒あたり100クエリで、20接続がある場合、各SQL実行にかかる時間は200ミリ秒未満です(100クエリを実行するには、20接続=>壁時計時間の1秒あたり合計20秒の作業)。

12
AngerClown

(休止状態を介した)C3P0の構成に関係なく、MySQL自体によって制限が課される場合があります。デフォルトでは、MySQLで許可される接続の最大数は100であることに注意してください。したがって、C3P0に最大200、500、または1000の接続をプールするように指示しても、これは達成できません。以下を使用してMySQLシェルを開きます。

$ msql -u [user] -p

そして、許可される接続の最大数を取得するには、次のように入力します。

$ show variables where Variable_name='max_connections';

返される数がアプリケーションに対して少なすぎる場合は、変更することを検討してください(通常、Linuxシステムの/ etc/mysql /内にあるmy.cnfファイルを編集します)。

3

私もこの問題を抱えていて、C3P0のプロパティcheckoutTimeoutを0より大きい値ではなく0に設定することで解決しました。

実際、接続を待機しているスレッドがたくさんあり、10秒後に、あなたと同じエラーが発生しました。

こちらのドキュメントを参照してください: http://www.mchange.com/projects/c3p0/#checkoutTimeout

0
c4k

私もこの問題を抱えていました。/etc/hostsエントリが変更されたため、ユーザーがホストの権限を持っていないことが原因でした。

0
Martin Krueger