1. 程式人生 > >使用c3p0與DBCP連線池,造成的MySql 8小時問題解決方案

使用c3p0與DBCP連線池,造成的MySql 8小時問題解決方案

本文提供了對c3p0與DBCP連線池連線MySQL資料庫時, 8小時內無請求自動斷開連線的解決方案。首先介紹一下我在專案(c3p0連線池)中遇到的問題,後面還提供了使用DBCP連線池的解決方案。

基本問題解決

專案環境:

Java Web專案框架為spring MVC+JPA,使用c3p0連線池,釋出環境為Tomcat 7

錯誤描述:

專案執行一段時間(大概幾個小時)之後訪問時會出現第一次訪問報錯,再次訪問正常的現象,且多次出現此問題。

報錯日誌:

  1. org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin transaction failed:   
  2.     at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:428)  
  3.     at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)  
  4.     at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:417)  
  5.     at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:255)  
  6.     at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)  
  7.     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)  
  8.     at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)  
  9.     at com.appcarcare.cube.service.UserService EnhancerByCGLIB a4429cba.getUserDao(<generated>)  
  10.     at com.appcarcare.cube.servlet.DataCenterServlet$SqlTimer.connectSql(DataCenterServlet.java:76)  
  11.     at com.appcarcare.cube.servlet.DataCenterServlet$SqlTimer.run(DataCenterServlet.java:70)  
  12.     at java.util.TimerThread.mainLoop(Timer.java:555)  
  13.     at java.util.TimerThread.run(Timer.java:505)  
  14. Caused by: javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin transaction failed:   
  15.     at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)  
  16.     at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)  
  17.     at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:1397)  
  18.     at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:62)  
  19.     at org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:71)  
  20.     at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:60)  
  21.     at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:378)  
  22.     ... 11 more  
  23. Caused by: org.hibernate.TransactionException: JDBC begin transaction failed:   
  24.     at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:76)  
  25.     at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:160)  
  26.     at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1426)  
  27.     at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:59)  
  28.     ... 14 more  
  29. Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure  
  30. The last packet successfully received from the server was 1,836,166 milliseconds ago.  The last packet sent successfully to the server was 29,134 milliseconds ago.  
  31.     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
  32.     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)  
  33.     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
  34.     at java.lang.reflect.Constructor.newInstance(Constructor.java:526)  
  35.     at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)  
  36.     at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1117)  
  37.     at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3567)  
  38.     at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3456)  
  39.     at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3997)  
  40.     at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)  
  41.     at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)  
  42.     at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2713)  
  43.     at com.mysql.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl.java:5060)  
  44.     at com.mchange.v2.c3p0.impl.NewProxyConnection.setAutoCommit(NewProxyConnection.java:881)  
  45.     at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:72)  
  46.     ... 17 more  
  47. Caused by: java.net.SocketException: Software caused connection abort: recv failed  
  48.     at java.net.SocketInputStream.socketRead0(Native Method)  
  49.     at java.net.SocketInputStream.read(SocketInputStream.java:150)  
  50.     at java.net.SocketInputStream.read(SocketInputStream.java:121)  
  51.     at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)  
  52.     at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)  
  53.     at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)  
  54.     at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3014)  
  55.     at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3467)  
  56.     ... 25 more  

原因分析:

MySQL伺服器預設的“wait_timeout”是28800秒即8小時,意味著如果一個連線的空閒時間超過8個小時,MySQL將自動斷開該連線,而連線池卻認為該連線還是有效的(因為並未校驗連線的有效性),當應用申請使用該連線時,就會導致上面的報錯。

解決方案(解決這個問題的辦法有三種,推薦第二種):

1. 增加 MySQL 的 wait_timeout 屬性的值 

修改mysql安裝目錄下的配置檔案 my.ini檔案(如果沒有此檔案,複製“my-default.ini”檔案,生成“復件 my-default.ini”檔案。將“復件 my-default.ini”檔案重新命名成“my.ini” ),在檔案中設定: 
  1. wait_timeout=31536000  
  2. interactive_timeout=31536000  
這兩個引數的預設值是8小時(60*60*8=28800)。 注意:1.wait_timeout的最大值只允許2147483 (24天左右) 2.修改配置檔案為網上大部分文章所提供的方式,也可以使用mysql命令對這兩個屬性進行修改

2. 減少連線池內連線的生存週期

減少連線池內連線的生存週期,使之小於上一項中所設定的wait_timeout 的值 修改 c3p0 的配置檔案,在 Spring 的配置檔案中設定:
  1. <bean id="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">       
  2. <property name="maxIdleTime"value="1800"/>    
  3. <!--other properties -->    
  4. </bean>  

3. 定期使用連線池內的連線

定期使用連線池內的連線,使得它們不會因為閒置超時而被 MySQL 斷開。 
修改 c3p0 的配置檔案,在 Spring 的配置檔案中設定:
  1.        <bean id="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">    
  2. <property name="preferredTestQuery" value="SELECT 1"/>    
  3. <property name="idleConnectionTestPeriod" value="18000"/>    
  4. <property name="testConnectionOnCheckout" value="true"/>    
  5. </bean>  

知識擴充套件

C3P0

C3P0是一個開放原始碼的JDBC連線池,它在lib目錄中與hibernate一起釋出,包括了實現jdbc3和jdbc2擴充套件規範說明的Connection 和Statement 池的DataSources 物件。 c3p0配置檔案

  1. <default-config>
  2.   <!--當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default: 3 -->
  3.   <propertyname="acquireIncrement">3</property>
  4.   <!--定義在從資料庫獲取新連線失敗後重復嘗試的次數。Default: 30 -->
  5.   <propertyname="acquireRetryAttempts">30</property>
  6.   <!--兩次連線中間隔時間,單位毫秒。Default: 1000 -->
  7.   <propertyname="acquireRetryDelay">1000</property>
  8.   <!--連線關閉時預設將所有未提交的操作回滾。Default: false -->
  9.   <propertyname="autoCommitOnClose">false</property>
  10.   <!--c3p0將建一張名為Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個引數那麼   
  11.   屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試   
  12.   使用。Default: null-->
  13.   <propertyname="automaticTestTable">Test</property>
  14.   <!--獲取連線失敗將會引起所有等待連線池來獲取連線的執行緒丟擲異常。但是資料來源仍有效   
  15.   保留,並在下次呼叫getConnection()的時候繼續嘗試獲取連線。如果設為true,那麼在嘗試   
  16.   獲取連線失敗後該資料來源將申明已斷開並永久關閉。Default: false-->
  17.   <