11

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>

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

ここには実際には 2 つのオプションしかありません。完了時に接続を解放するわけではありません。または、彼女のズボンに入ろうとしているかのように、db でチャットをぶらぶらしています。誰かが何かアイデアを持っているなら、私は感謝しますthx

フォローアップ:最終的に、OpenSessionInViewInterceptor を使用したために接続がリークしていたことが判明しました。私は春のセキュリティをフィルターとして実行していたので、DBに接続してそれらを閉じません。修正は、OpenSessionInViewInterceptor を OpenSessionInViewFilter に移動することでした。

4

5 に答える 5

12

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

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

于 2011-03-08T20:02:11.520 に答える
5

接続がリークする可能性はほとんどありません@Transactional。さもなければ、最初の 100 件のリクエストの後にサイトが機能しなくなります。

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

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

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

于 2011-03-08T17:21:39.063 に答える
3

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 ファイルを編集します)。

于 2011-03-18T00:39:51.187 に答える
0

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

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

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

于 2013-07-12T12:52:15.157 に答える
0

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

于 2011-11-14T15:18:35.923 に答える