數據庫鏈接超時(默認8小時)報錯:MySQLNonTransientConnectionException

myBatis連接MySQL報異常:No operations allowed after connection closed.Connection was implicitly closed

 

異常信息

?
mybatis錯誤如下:

HTTP Status 500 - Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: 

Could not open JDBC Connection for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.


type Exception report

message Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection 

for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.

description The server encountered an internal error that prevented it from fulfilling this request.

exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is
	org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is 
	com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

root cause

org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is 
	com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
	org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:243)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
	org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:420)
	org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:257)
	org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
	org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	com.sun.proxy.$Proxy24.news(Unknown Source)
	com.cn.article.controller.ArticleInfoController.news2(ArticleInfoController.java:691)
	sun.reflect.GeneratedMethodAccessor171.invoke(Unknown Source)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:601)
	org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
	org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
	org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

root cause

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 50,614,951 milliseconds ago.  
	The last packet sent successfully to the server was 50,614,951 milliseconds ago. is longer than the server configured value of 'wait_timeout'.
	You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured 
	values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
	sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
	sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	java.lang.reflect.Constructor.newInstance(Constructor.java:525)
	com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
	com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1127)
	com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3983)
	com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2596)
	com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2776)
	com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2832)
	com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2781)
	com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1569)
	com.mysql.jdbc.DatabaseMetaData.getUserName(DatabaseMetaData.java:7013)
	org.apache.commons.dbcp.DelegatingConnection.toString(DelegatingConnection.java:104)
	org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.toString(PoolingDataSource.java:344)
	java.lang.String.valueOf(String.java:2854)
	java.lang.StringBuilder.append(StringBuilder.java:128)
	org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:207)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
	org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:420)
	org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:257)
	org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
	org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	com.sun.proxy.$Proxy24.news(Unknown Source)
	com.cn.article.controller.ArticleInfoController.news2(ArticleInfoController.java:691)
	sun.reflect.GeneratedMethodAccessor171.invoke(Unknown Source)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:601)
	org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
	org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
	org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)


hibernate錯誤如下:
org.hibernate.exception.JDBCConnectionException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:74)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
.......
Caused by: com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: No operations allowed after connection closed.Connection was implicitly closed due to underlying exception/error:
** BEGIN NESTED EXCEPTION **
com.mysql.jdbc.CommunicationsException
MESSAGE: Communications link failure due to underlying exception:
** BEGIN NESTED EXCEPTION **
java.net.SocketException
MESSAGE: Broken pipe
STACKTRACE:
java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
......
** END NESTED EXCEPTION **

原因分析

查看了Mysql的文檔,以及Connector/J的文檔以及在線說明發現,出現這種異常的原因是:

  Mysql服務器默認的“wait_timeout”是8小時,也就是說一個connection空閒超過8個小時,Mysql將自動斷開該connection。這就是問題的所在,在C3P0 pools中的connections如果空閒超過8小時,Mysql將其斷開,而C3P0並不知道該connection已經失效,如果這時有Client請求connection,C3P0將該失效的Connection提供給Client,將會造成上面的異常。

解決方案

解決的方法有3種:

  1. 增加 wait_timeout 的時間。
  2. 減少 Connection pools 中 connection 的 lifetime。
  3. 測試 Connection pools 中 connection 的有效性。
  4. 自己最終解決:將原來的DBCP連接池改爲C3p0連接池,並增c3p0對於maxIdleTime時間的控制,一定要小於8小時

當然最好的辦法是同時綜合使用上述方法,下面就 DBCP、C3P0 和 simple jdbc dataSource 分別做一說明,假設 wait_timeout 爲

默認的8小時

DBCP 增加以下配置信息:

複製代碼
validationQuery = "select 1"

testWhileIdle = "true"

//some positive integer
timeBetweenEvictionRunsMillis = 3600000

//set to something smaller than 'wait_timeout'
minEvictableIdleTimeMillis = 18000000

//if you don't mind a hit for every getConnection(), set to "true"
testOnBorrow = "true"
複製代碼

C3P0 增加以下配置信息:

複製代碼
//獲取connnection時測試是否有效
testConnectionOnCheckin = true

//自動測試的table名稱
automaticTestTable=C3P0TestTable

//set to something much less than wait_timeout, prevents connections from going stale
idleConnectionTestPeriod = 18000

//set to something slightly less than wait_timeout, preventing 'stale' connections from being handed out
maxIdleTime = 25000

//if you can take the performance 'hit', set to "true"
testConnectionOnCheckout = true

c3p0配置

(推薦)

使用第三方數據庫連接池: 現在第三方數據庫連接池使用較多的爲c3p0,proxool等,在性能上c3p0稍好一些,原因c3p0數據庫連接池,

底層有一個定時查看數據庫連接是否有效的參數。而且Hibernate的api中也推薦使用第三方數據庫連接池,

因爲Hibernate本身的數據庫連接池過於簡單、本身存在bug。c3p0參數配置如下:

<!-- lang: xml -->
<!-- 數據源 --> 
<bean id="dataSourceTarget" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="user" value="root" />
<property name="password" value="root" />
<property name="jdbcUrl" value="jdbc:mysql://192.168.61.208:3306/sinoomv1_0_0" />
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<!-- 系統初始化連接數 -->
<property name="initialPoolSize" value="10" />
<!-- 最大連接數 -->
<property name="maxPoolSize" value="30" />
<!-- 最小連接數 -->
<property name="minPoolSize" value="10" />
<!--最大空閒時間,600秒(10分鐘)內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->
<property name="maxIdleTime" value="600" />
<!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->  
<property name="acquireIncrement" value="3" />
<!--每60秒檢查所有連接池中的空閒連接。Default: 0 -->  
<property name="idleConnectionTestPeriod" value="60" />
<!-- 每次從pool內checkout連接時測試有效性(同步操作)
    程序每次數據庫調用都連接有效性,若無效關閉此連接並剔除出pool,
   從pool內取其他連接,慎用,會造成至少多一倍的數據庫調用。Default:false -->
<property name="testConnectionOnCheckout" value="false" />
<!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30 -->
<property name="acquireRetryAttempts" value="30"/>
<!--兩次連接中間隔時間,單位毫秒。Default: 1000 -->
<property name="acquireRetryDelay" value="1000"/>
</bean>

查看mysql數據庫連接池情況,命令如下:

<!-- lang: sql -->
Mysql> show processlist;

ps:最近在在停起程序的時候我發現了一個問題,就算是我停止了服務,但是程序和mysql數據庫之間的連接數依然沒有斷開,

後來經查資料和同事請教,我發現在上邊的c3p0配置存在一個小bug。因爲我將c3p0交給spring進行管理,

但是在上邊注入的bean中沒有加上destroy-method="close"這個屬性。 也就是說Spring是不能正常銷燬連接的,由此出現了我說的問題。

寫在這裏以供大家警示,真是馬虎啊,竟然忘記了銷燬,實在不應該。

<!-- lang: xml -->
<!-- 數據源 --> 
<bean id="dataSourceTarget" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
 ... ...
</bean>

4.自己實際解決方案:

4.1Spring整合c3p0 配置文件Demo如下:

  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <beans xmlns="http://www.springframework.org/schema/beans"    
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  4.     xmlns:jee="http://www.springframework.org/schema/jee"    
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd    
  6.             http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">    
  7.     <bean id="dataSource"    
  8.         class="com.mchange.v2.c3p0.ComboPooledDataSource"    
  9.         destroy-method="close">    
  10.         <property name="driverClass">    
  11.             <value>com.mysql.jdbc.Driver</value>    
  12.         </property>    
  13.         <property name="jdbcUrl">    
  14.             <value>jdbc:mysql://192.168.3.110:3306/DBName?useUnicode=true&characterEncoding=GBK</value>    
  15.         </property>    
  16.         <property name="user">    
  17.             <value>root</value>    
  18.         </property>    
  19.         <property name="password">    
  20.             <value>root</value>    
  21.         </property>    
  22.      
  23.  <!--連接池中保留的最小連接數。-->    
  24.         <property name="minPoolSize">    
  25.             <value>5</value>    
  26.         </property>    
  27.      
  28.  <!--連接池中保留的最大連接數。Default: 15 -->    
  29.         <property name="maxPoolSize">    
  30.             <value>30</value>    
  31.         </property>    
  32.      
  33. <!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->    
  34.         <property name="initialPoolSize">    
  35.             <value>10</value>    
  36.         </property>    
  37.      
  38.  <!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->    
  39.         <property name="maxIdleTime">    
  40.             <value>60</value>    
  41.         </property>    
  42.      
  43.  <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->    
  44.         <property name="acquireIncrement">    
  45.             <value>5</value>    
  46.         </property>    
  47.      
  48.  <!--JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements    
  49.   屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。    
  50.   如果maxStatements與maxStatementsPerConnection均爲0,則緩存被關閉。Default: 0-->    
  51.         <property name="maxStatements">    
  52.             <value>0</value>    
  53.         </property>    
  54.      
  55.  <!--每60秒檢查所有連接池中的空閒連接。Default: 0 -->    
  56.         <property name="idleConnectionTestPeriod">    
  57.             <value>60</value>    
  58.         </property>    
  59.      
  60.  <!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30 -->    
  61.         <property name="acquireRetryAttempts">    
  62.             <value>30</value>    
  63.         </property>    
  64.      
  65.  <!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效    
  66.   保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試    
  67.   獲取連接失敗後該數據源將申明已斷開並永久關閉。Default: false-->    
  68.         <property name="breakAfterAcquireFailure">    
  69.             <value>true</value>    
  70.         </property>    
  71.      
  72.  <!--因性能消耗大請只在需要的時候使用它。如果設爲true那麼在每個connection提交的    
  73.   時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable    
  74.   等方法來提升連接測試的性能。Default: false -->    
  75.         <property name="testConnectionOnCheckout">    
  76.             <value>false</value>    
  77.         </property>    
  78.     </bean>    
  79. </beans>   

 4.2Maven的pom.xml文件中引入c3p0依賴:

<!-- 數據庫連接池包-->      <dependency>          <groupId>com.mchange</groupId>          <artifactId>c3p0</artifactId>          <version>0.9.5-pre8</version>      </dependency>  注意:可以配置最大空閒時間小一些如下60秒

  1. <!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->    
  2.         <property name="maxIdleTime">    
  3.             <value>60</value>    
  4.         </property>  

4.3 在項目日誌文件中配置debug模式,其中DEBUG模式下可能會報java.io.FileNotFoundException錯誤信息(說c3p0找不到c3p0.properties;mchange-log.properties;mchange-commons.properties等文件),這是因爲在DEBUG模式下c3p0會打印出具體的警告信息(這不是報錯,這是警告信息,不影響運行),其中c3p0官方推薦INFO級別日誌打印級別。在eclipse控制檯中便可以查看c3p0數據庫連接池中連接的獲取(默認最少有5個空閒,name一分鐘之內有五個connection被廢除)和廢除情況,這樣就可以保持Spring維護的數據庫連接池中的連接都是最新的。輸出如下:

[com.mchange.v2.c3p0.impl.NewPooledConnection] - com.mchange.v2.c3p0.impl.NewPooledConnection@369d34 closed by a client. java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:659) at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:621) at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1024) at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696) [com.mchange.v2.resourcepool.BasicResourcePool] - incremented pending_acquires: 2 [com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool] - Preparing to destroy PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@1908897 [com.mchange.v2.resourcepool.BasicResourcePool] - Starting acquisition series. Incremented pending_acquires [2],  attempts_remaining: 30 [com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool] - Successfully destroyed PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@369d34 [com.mchange.v2.c3p0.impl.NewPooledConnection] - com.mchange.v2.c3p0.impl.NewPooledConnection@3b21e3 closed by a client. java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:659) at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:621) at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1024) at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696) [com.mchange.v2.async.ThreadPoolAsynchronousRunner] - com.mchange.v2.async.ThreadPoolAsynchronousRunner@1641371: Adding task to queue -- com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@c6a83d [com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool] - Successfully destroyed PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@3b21e3 [com.mchange.v2.resourcepool.BasicResourcePool] - Successfully destroyed resource: com.mchange.v2.c3p0.impl.NewPooledConnection@369d34 [com.mchange.v2.resourcepool.BasicResourcePool] - Successfully destroyed resource: com.mchange.v2.c3p0.impl.NewPooledConnection@3b21e3 [com.mchange.v2.resourcepool.BasicResourcePool] - incremented pending_acquires: 3 [com.mchange.v2.c3p0.impl.NewPooledConnection] - com.mchange.v2.c3p0.impl.NewPooledConnection@1908897 closed by a client. java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:659) at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:621) at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1024) at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696)

simple jdbc dataSource 增加以下配置信息:

複製代碼
Pool.PingQuery = select 1

Pool.PingEnabled = true

Pool.PingConnectionsOlderThan = 0

//對於空閒的連接一個小時檢查一次
Pool.PingConnectionsNotUsedFor = 3600000
複製代碼

其他方案(不推薦)

  對於 MySQL5 之前的版本,如 Mysql4.x,只需要修改連接池配置中的 URL,添加一個參數:autoReconnect=true(如jdbc:mysql://hostaddress:3306/schemaname?autoReconnect=true),如果是 MySQL5 及以後的版本,則需要修改 my.cnf(或者my.ini) 文件,在 [mysqld] 後面添加上:

?
wait_timeout = n
interactive-timeout = n

其中 n 爲服務器關閉交互式連接前等待活動的秒數。可是就部署而言每次修改 my.ini 比較麻煩,而且 n 等於多少纔是合適的值呢? 所以並不推薦這個解決辦法。)

 

hibernate的解決方案: 
<property name="connection.autoReconnect">true</property>  <!--這個是最主要的--> 
<property name="connection.autoReconnectForPools">true</property> 
<property name="connection.is-connection-validation-required">true</property> 
加入以上property,可解決此問題,如果未使用hibernate等持久化管理框架,可在mysql的url中加入autoReconnect=true,這樣就可以解決。
原 因很簡單。在對數據庫操作結束後關閉連接是正確的做法,沒什麼大問題。至於出現:No operations allowed after connection closed。這樣的問題原因只有一個,你這裏和數據庫的連接Connection是一個Static的,程序共享這一個Connection。所以第一 次對數據庫操作沒問題,當把Connection關閉後,第二次還想操作數據庫時Connection肯定不存在了。



-------------------------相關-------------------------

 

1. 即使在創建Mysql時url中加入了autoReconnect=true參數,一但這個連接兩次訪問數據庫的時間超出了服務器端wait_timeout的時間限制,還是會CommunicationsException: The last packet successfully received from the server was xxx milliseconds ago. 
2. 服務器端的參數可以用 
  show global variables like 'wait_timeout'; 
  set global wait_timeout=10; 
  來進行設置,但是wait_timeout值不應該設的太高. 
3. 較好的策略是對處於idle狀態的connection定時發送一個sql,來刷新服務器上的時間戳.這可以使用c3p0r的連接池.http://bzhang.iteye.com/blog/321832 
4. 對於tomcat的server.xml中使用的連接池,http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html,http://commons.apache.org/dbcp/configuration.html使用DBCP的連接池可以採用 
<Resource name="jdbc/test" auth="Container"  
              type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver"  
              url="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8"  
              username="root" password="test" maxActive="500" maxIdle="10"  
              maxWait="-1" timeBetweenEvictionRunsMillis="10000" minEvictableIdleTimeMillis="10000" /> 
  4.1 設置validationQuery,這樣每次borrow(默認爲開啓)時會通過這個sql校驗連接的有效性,但是增加了時間. 
  4.2 設置timeBetweenEvictionRunsMillis="10000" minEvictableIdleTimeMillis="10000" 依賴evictor thread線程來把超時的連接關閉. 
  4.3 設置testWhileIdle="true" timeBetweenEvictionRunsMillis="10000" validationQuery="select 1" 使得定時去用query檢測處於idle狀態的連接,也就刷新了服務器端的時間. 

5.每次提交的最大packet大小 
show global variables like 'max_allowed_packet'; 
set global max_allowed_packet=1024*1024; 

6. SQLyog 中連接參數的設置 
  6.1 在SQLyog中的設置 set autocommit=0,這樣當前連接的自動提交爲false,可以控制事務了. 
  6.2 begin; 事務開始 
  6.3 select * from test where 1=1 and id =1 for update;這樣就把選到的記錄行鎖上了,再開一個SQLyog,也執行以上相同的操作,就會一直wait在那裏. 
  6.4 commit; 提交 
  6.5 rollback; 回滾 
  6.6 set autocommit=0;後應該加上 
      set transaction isolation level read committed; 
      這樣其它客戶端就能看到commit的數據, 
  疑問: 
      如果不設置set transaction isolation level read committed;如果兩個客戶端都select 相同的數據,一個客戶端修改然後提交,另一個客戶端不提交當前事務的前提下,去執行select ,取不到另一客戶端提交的數據,不知道SQLyog默認的事務級別是什麼樣的. 

7. SQLyog中查看mysql的狀態,show global variables like '%lock%'; 是個好方法.對於事務鎖(例如for update)報Lock wait timeout exceeded ,只能通過修改my.ini文件innodb_lock_wait_timeout = 100;才能生效. 

8. linux下修改用戶密碼 mysqladmin -u root password "new_pass"


三大連接池說明:

1. Apache-DBCP

BasicDataSource 相關的參數說明

dataSource: 要連接的 datasource (通常我們不會定義在 server.xml)
defaultAutoCommit: 對於事務是否 autoCommit, 默認值爲 true
defaultReadOnly: 對於數據庫是否只能讀取, 默認值爲 false
driverClassName:連接數據庫所用的 JDBC Driver Class,
maxActive: 可以從對象池中取出的對象最大個數,爲0則表示沒有限制,默認爲8
maxIdle: 最大等待連接中的數量,設 0 爲沒有限制 (對象池中對象最大個數)
minIdle:對象池中對象最小個數
maxWait: 最大等待秒數, 單位爲 ms, 超過時間會丟出錯誤信息
password: 登陸數據庫所用的密碼
url: 連接數據庫的 URL
username: 登陸數據庫所用的帳號
validationQuery: 驗證連接是否成功, SQL SELECT 指令至少要返回一行
removeAbandoned: 是否自我中斷, 默認是 false
removeAbandonedTimeout: 幾秒後會自我中斷, removeAbandoned 必須爲 true
logAbandoned: 是否記錄中斷事件, 默認爲 false
minEvictableIdleTimeMillis:大於0 ,進行連接空閒時間判斷,或爲0,對空閒的連接不進行驗證;默認30分鐘
timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔,如果小於等於0,不會啓動檢查線程,默認-1
testOnBorrow:取得對象時是否進行驗證,檢查對象是否有效,默認爲false
testOnReturn:返回對象時是否進行驗證,檢查對象是否有效,默認爲false
testWhileIdle:空閒時是否進行驗證,檢查對象是否有效,默認爲false


在使用DBCP的時候,如果使用默認值,則數據庫連接因爲某種原因斷掉後,再從連接池中取得連接又不進行驗證,這時取得的連接實際上就會是無效的數據庫連接。因此爲了防止獲得的數據庫連接失效,在使用的時候最好保證:


username: 登陸數據庫所用的帳號
validationQuery:SELECT COUNT(*) FROM DUAL
testOnBorrow、testOnReturn、testWhileIdle:最好都設爲true
minEvictableIdleTimeMillis:大於0 ,進行連接空閒時間判斷,或爲0,對空閒的連接不進行驗證
timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔,如果小於等於0,不會啓動檢查線程
PS:在構造GenericObjectPool [BasicDataSource在其createDataSource () 方法中也會使用GenericObjectPool] 時,會生成一個內嵌類Evictor,實現自Runnable接口。如果timeBetweenEvictionRunsMillis大於0,每過timeBetweenEvictionRunsMillis毫秒Evictor會調用evict()方法,檢查對象的閒置時間是否大於 minEvictableIdleTimeMillis毫秒(_minEvictableIdleTimeMillis小於等於0時則忽略,默認爲30分鐘),是則銷燬此對象,否則就激活並校驗對象,然後調用ensureMinIdle方法檢查確保池中對象個數不小於_minIdle。在調用returnObject方法把對象放回對象池,首先檢查該對象是否有效,然後調用PoolableObjectFactory 的passivateObject方法使對象處於非活動狀態。再檢查對象池中對象個數是否小於maxIdle,是則可以把此對象放回對象池,否則銷燬此對象
上述特性的可設置性已在代碼中驗證,具體性能是否能實現有待實際驗證

2. C3P0

C3P0的官方example中使用的數據源爲ComboPooledDataSource,網上一篇文章詳細介紹了C3P0連接池配置中各項含義[這些配置項的含義在下載解壓c3p0的壓縮包之後目錄的doc\index.html中的Configuration部分也有詳細的介紹,這裏偷下懶:P],現摘錄如下:
<c3p0-config>
<default-config>


<!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
<property name="acquireIncrement">3</property>


<!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30 -->
<property name="acquireRetryAttempts">30</property>


<!--兩次連接中間隔時間,單位毫秒。Default: 1000 -->
<property name="acquireRetryDelay">1000</property>


<!--連接關閉時默認將所有未提交的操作回滾。Default: false -->
<property name="autoCommitOnClose">false</property>


<!--c3p0將建一張名爲Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數那麼
屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試
使用。Default: null-->
<property name="automaticTestTable">Test</property>

<!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效
保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試
獲取連接失敗後該數據源將申明已斷開並永久關閉。Default: false-->
<property name="breakAfterAcquireFailure">false</property>


<!--當連接池用完時客戶端調用getConnection()後等待獲取新連接的時間,超時後將拋出
SQLException,如設爲0則無限期等待。單位毫秒。Default: 0 -->
<property name="checkoutTimeout">100</property>


<!--通過實現ConnectionTester或QueryConnectionTester的類來測試連接。類名需制定全路徑。
Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName"></property>

<!--指定c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那麼無需設置,默認null即可
Default: null-->
<property name="factoryClassLocation">null</property>

<!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
(文檔原文)作者強烈建議不使用的一個屬性-->
<property name="forceIgnoreUnresolvedTransactions">false</property>

<!--每60秒檢查所有連接池中的空閒連接。Default: 0 -->
<property name="idleConnectionTestPeriod">60</property>

<!--初始化時獲取三個連接,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
<property name="initialPoolSize">3</property>

<!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->
<property name="maxIdleTime">60</property>

<!--連接池中保留的最大連接數。Default: 15 -->
<property name="maxPoolSize">15</property>

<!--JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements
屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。
如果maxStatements與maxStatementsPerConnection均爲0,則緩存被關閉。Default: 0-->
<property name="maxStatements">100</property>

<!--maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數。Default: 0 -->
<property name="maxStatementsPerConnection"></property>

<!--c3p0是異步操作的,緩慢的JDBC操作通過幫助進程完成。擴展這些操作可以有效的提升性能
通過多線程實現多個操作同時被執行。Default: 3-->
<property name="numHelperThreads">3</property>

<!--當用戶調用getConnection()時使root用戶成爲去獲取連接的用戶。主要用於連接池連接非c3p0
的數據源時。Default: null-->
<property name="overrideDefaultUser">root</property>

<!--與overrideDefaultUser參數對應使用的一個參數。Default: null-->
<property name="overrideDefaultPassword">password</property>

<!--密碼。Default: null-->
<property name="password"></property>

<!--定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意:
測試的表必須在初始數據源的時候就存在。Default: null-->
<property name="preferredTestQuery">select id from test where id=1</property>

<!--用戶修改系統配置參數執行前最多等待300秒。Default: 300 -->
<property name="propertyCycle">300</property>

<!--因性能消耗大請只在需要的時候使用它。如果設爲true那麼在每個connection提交的
時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
等方法來提升連接測試的性能。Default: false -->
<property name="testConnectionOnCheckout">false</property>

<!--如果設爲true那麼在取得連接的同時將校驗連接的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>

<!--用戶名。Default: null-->
<property name="user">root</property>

<!--早期的c3p0版本對JDBC接口採用動態反射代理。在早期版本用途廣泛的情況下這個參數
允許用戶恢復到動態反射代理以解決不穩定的故障。最新的非反射代理更快並且已經開始
廣泛的被使用,所以這個參數未必有用。現在原先的動態反射與新的非反射代理同時受到
支持,但今後可能的版本可能不支持動態反射代理。Default: false-->
<property name="usesTraditionalReflectiveProxies">false</property>


<property name="automaticTestTable">con_test</property>
    <property name="checkoutTimeout">30000</property>
    <property name="idleConnectionTestPeriod">30</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">25</property>
    <property name="minPoolSize">10</property>
    <property name="maxStatements">0</property>
    <user-overrides user="swaldman">
    </user-overrides>
</default-config>
<named-config name="dumbTestConfig">
    <property name="maxStatements">200</property>
    <user-overrides user="poop">
      <property name="maxStatements">300</property>
    </user-overrides>
   </named-config>
</c3p0-config>
上述特性的可設置性已在代碼中驗證,具體性能是否能實現有待實際驗證
從配置項的內容來看,C3P0和DBCP都有比較詳細的有關連接檢測保證的配置,我們可以看到C3P0可以控制數據源內加載的PreparedStatements數量,並且可以設置幫助線程的數量來提升JDBC操作的速度,這些是DBCP未提供的;另外從網絡上的評價來看,DBCP出現Bug的頻率要大於C3P0,不過這一點有待於我們自己實際的檢測。

3. Proxool

Proxool的使用和dbcp以及c3p0稍有不同,我們需要並且只需要在使用基本的java.sql.DriverManager之前加載org.logicalcobwebs.proxool.ProxoolDriver驅動類,並且按照proxool定義的url格式 ["proxool." + alias + ":" + driverClass + ":" + driverUrl ,其中alias是爲連接池自定義的別名] 來獲得connection;具體的可以參看proxool doc下的UserGuide,或本文所附的示例代碼。下面對連接池的特性配置作詳細說明 [這個是自己翻譯的,不一定準確,有問題時請參看doc下的Properties ~]。

fatal-sql-exception

以逗號隔開的異常列表,當設置了此項之後,每當出現SQLException時都將與列表中異常項作比較,如果匹配則認爲出現fatal異常,這將導致connection被丟棄,並且不論出現任何情況該異常將被重拋一次以通知用戶發生的情況。默認值爲null

fatal-sql-exception-wrapper-class

如果配置了fatal-sql-exception,則默認的操作是丟 棄引起SQLException的原因而只是拋出原始異常。使用fatal-sql-exception-wrapper-class這個特性可以將 SQLException包裝到繼承SQLException或RunTimeException的任何異常類裏。Proxool提供了兩個類供使用 FatalSQLException和FatalRunTimeException;使用這兩個類的話就將該選項設置爲 'org.logicalcobwebs.proxool.FatalSQLException'或者 'org.logicalcobwebs.proxool.FatalRuntimeException'。默認值爲null

house-keeping-sleep-time

proxool自動偵察各個連接狀態的時間間隔(毫秒),偵察到空閒的連接就馬上回收,超時的銷燬,默認值爲30秒

house-keeping-test-sql

如果偵察線程發現閒置連接,則會使用這個SQL語句來對這些連接進行檢查;這項設置的語句應該能夠被很快的執行,例如查詢當前時間 [info.setProperty("proxool.house-keeping-test-sql", "select CURRENT_DATE");] 。如果不設置則該選項被忽略

injectable-connection-interface、injectable-statement-interface、injectable-prepared-statement-interface、injectable-callable-statement-interface

jmx

如果此項設爲true,則連接池將被以名稱"Proxool:type=Pool, name=<alias>"註冊爲JMS Server的MBean。默認值爲false

jmx-agent-id

當且僅當jmx選項設爲true時使用,爲以逗號分隔的連接持註冊到的JMS代理名稱列表;如果不設置則所有註冊的JMX Server都將被使用

maximum-active-time

線程最大存活時間,超過此時間的線程將被守護線程kill掉,默認值爲5分鐘

maximum-connection-count

到數據庫的最大連接數,超過了這個連接,再有請求時,就排在隊列中等候,最大的等待請求數由simultaneous-build-throttle決定;默認值爲15

maximum-connection-lifetime

連接最大存活時間,毫秒爲單位,默認值爲4小時

minimum-connection-count

不管是否被使用都保持開放的最小連接數,默認值爲5

overload-without-refusal-lifetime

用來判斷連接池狀態,如果在此選項設置時間內(毫秒爲單位)拒絕了連接,則認爲過負載。默認值爲60秒

prototype-count

最少保持的空閒連接數,注意與minimum-connection-count區分。默認值爲0

simultaneous-build-throttle

最大的等待請求數,默認值爲10

test-before-use

如果設爲true則connection在使用前將以house-keeping-test-sql設置的語句測試,如果測試不通過則該connection被丟棄並會重新分配一個connection。默認爲false

test-after-use

如果設爲true則connection在關閉(放回連接池)前將以house-keeping-test-sql設置的語句測試,如果測試不通過connection將被丟棄。默認值爲false

與其它連接池特性的設置方法不同,Proxool不提供相應的set方法,所有特性都要以諸如info.setProperty("proxool.jmx", "false");方式設定

上述特性的可設置性已在代碼中驗證,具體性能是否能實現有待實際驗證


引自原網址:http://fengbin2005.iteye.com/blog/1906488

發佈了15 篇原創文章 · 獲贊 14 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章