DbcpからTomcatjdbc接続プールに移行しました。負荷のかかったシステムを試したところ、次の例外が発生しました。
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&useDynamicCharsetInfo=false&rewriteBatchedStatements=true&useLocalSessionState=true&useLocalTransactionState=true&alwaysSendSetIsolation=false&cacheServerConfiguration=true&noAccessToProcedureBodies=true&useUnicode=true&characterEncoding=UTF-8"
username="root" validationQuery="SELECT 1"/>
env:ubuntuおよびTomcat6。db-mysql
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;
}
}
したがって、これによると、接続はNullです。
con
の値は、次の行で取得されます。
PooledConnection con = idle.poll();
コードを追跡すると、idle
が(構成によって異なりますが、デフォルトで) FairBlockingQueue であることがわかります。ヒントについては、実装をチェックアウトできます。
一般に、常にResultSet、Statements、およびConnectionsを閉じる必要があり、使用された接続はプールに正しく解放される必要があります。これを正しく行わないと、接続が閉じられない=>再利用できなくなる可能性があります(接続プールの「リーク」)。
プールの状態について詳細なログを作成し、それを監視して問題を特定することをお勧めします。
データベース接続プールのリークを防ぐためのApacheのガイドライン:
removeAbandoned="true"
放棄されたデータベース接続は削除され、リサイクルされます
removeAbandonedTimeout="60"
データベース接続が放棄されたと見なされるまでにアイドル状態になっている秒数を設定します
logAbandoned="true"
データベース接続リソースを放棄したコードのスタックトレースをログに記録します。 「スタックトレースを生成する必要があるため、放棄された接続をログに記録すると、接続の借用ごとにオーバーヘッドが追加される」ことに注意してください。
maxWait
の値を少し増やすと(1200、1500、1700-実験だけで、ユーザーの観点からは応答時間に違いはありません)、まだ問題があるまれなケースが解消されると思います。
「ビジーでない接続はどこにありますか?」
それらが削除されたようで、何らかの理由で接続プールがそれらを再接続しようとしていません。
接続しているURLにこれを追加します。
autoReconnect=true
そして、これをプロパティとしてリソースに追加すると、切断された接続が自動的に再接続されます。
validationQuery="SELECT 1"
また、これにより、接続が切断されていることを確認できます。
logAbandoned="true"
スタックオーバーフローについては、同様の質問が複数あります。
Tomcat接続プール、アイドル接続、および接続作成JDBC接続プールがTomcatで接続を再開しない
ただし、接続が完全に解放されていない可能性もあります。これが接続の停止の原因です。 接続プールの枯渇を回避するためのJDBC MySql接続プールの方法
プールのバグのようです。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