bug回顧 :
想必大家在用MySQL時都會遇到連接超時的問題,如下圖所示:
### Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 47,795,922 milliseconds ago. The last packet sent successfully to the server was 47,795,922 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. ; SQL []; The last packet successfully received from the server was 47,795,922 milliseconds ago. The last packet sent successfully to the server was 47,795,922 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.; nested exception is com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 47,795,922 milliseconds ago. The last packet sent successfully to the server was 47,795,922 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.
大概意思是當前的connection所進行過的最新請求是在52,587秒之前,這個時間是大於服務所配置的wait_timeout時間的。
原因分析:
MySQL連接時,服務器默認的“wait_timeout”是8小時,也就是說一個connection空閑超過8個小時,Mysql將自動斷開該connection。connections如果空閑超過8小時,Mysql將其斷開,而DBCP連接池並不知道該connection已經失效,如果這時有Client請求connection,DBCP將該失效的Connection提供給Client,將會造成異常。
mysql分析:
打開MySQL的控制台,運行:show variables like ‘%timeout%’,查看和連接時間有關的MySQL系統變量,得到如下結果:
其中wait_timeout就是負責超時控制的變量,其時間為長度為28800s,就是8個小時,那么就是說MySQL的服務會在操作間隔8小時后斷開,需要再次重連。也有用戶在URL中使用jdbc.url=jdbc:mysql://localhost:3306/nd?autoReconnect=true來使得連接自動恢復,當然了,這是可以的,不過是MySQL4及其以下版本適用。MySQL5中已經無效了,必須調整系統變量來控制了。MySQL5手冊中對兩個變量有如下的說明:
interactive_timeout:服務器關閉交互式連接前等待活動的秒數。交互式客戶端定義為在mysql_real_connect()中使用CLIENT_INTERACTIVE選項的客戶端。又見wait_timeout
wait_timeout:服務器關閉非交互連接之前等待活動的秒數。在線程啟動時,根據全局wait_timeout值或全局interactive_timeout值初始化會話wait_timeout值,取決於客戶端類型(由mysql_real_connect()的連接選項CLIENT_INTERACTIVE定義),又見interactive_timeout
如此看來,兩個變量是共同控制的,那么都必須對他們進行修改了。繼續深入這兩個變量wait_timeout的取值范圍是1-2147483(Windows),1-31536000(linux),interactive_time取值隨wait_timeout變動,它們的默認值都是28800。
MySQL的系統變量由配置文件控制,當配置文件中不配置時,系統使用默認值,這個28800就是默認值。要修改就只能在配置文件里修改。Windows下在%MySQL HOME%/bin下有mysql.ini配置文件,打開后在如下位置添加兩個變量,賦值。(這里修改為388000)
解決方式:
1. 增加 MySQL 的 wait_timeout 屬性的值 (不推薦)
修改mysql安裝目錄下的配置文件 my.ini文件(如果沒有此文件,復制“my-default.ini”文件,生成“復件 my-default.ini”文件。將“復件 my-default.ini”文件重命名成“my.ini” ),在文件中設置:
wait_timeout=31536000 interactive_timeout=31536000
也可以使用mysql命令對這兩個屬性進行修改
2. 減少連接池內連接的生存周期
減少連接池內連接的生存周期,使之小於上一項中所設置的wait_timeout 的值。
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="maxIdleTime"value="1800"/> <!--other properties --> </bean>
3. 定期使用連接池內的連接
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="preferredTestQuery" value="SELECT 1"/> <property name="idleConnectionTestPeriod" value="18000"/> <property name="testConnectionOnCheckout" value="true"/> </bean>
附上dbcp和c3p0的標准配置
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://192.168.40.10:3336/XXX" /> <property name="username" value="" /> <property name="password" value="" /> <property name="maxWait" value="20000"></property> <property name="validationQuery" value="SELECT 1"></property> <property name="testWhileIdle" value="true"></property> <property name="testOnBorrow" value="true"></property> <property name="timeBetweenEvictionRunsMillis" value="3600000"></property> <property name="numTestsPerEvictionRun" value="50"></property> <property name="minEvictableIdleTimeMillis" value="120000"></property> <property name="removeAbandoned" value="true"/> <property name="removeAbandonedTimeout" value="6000000"/> </bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"><value>oracle.jdbc.driver.OracleDriver</value></property> <property name="jdbcUrl"><value>jdbc:oracle:thin:@localhost:1521:Test</value></property> <property name="user"><value>Kay</value></property> <property name="password"><value>root</value></property> <!--連接池中保留的最小連接數。--> <property name="minPoolSize" value="10" /> <!--連接池中保留的最大連接數。Default: 15 --> <property name="maxPoolSize" value="100" /> <!--最大空閑時間,1800秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 --> <property name="maxIdleTime" value="1800" /> <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 --> <property name="acquireIncrement" value="3" /> <property name="maxStatements" value="1000" /> <property name="initialPoolSize" value="10" /> <!--每60秒檢查所有連接池中的空閑連接。Default: 0 --> <property name="idleConnectionTestPeriod" value="60" /> <!--定義在從數據庫獲取新連接失敗后重復嘗試的次數。Default: 30 --> <property name="acquireRetryAttempts" value="30" /> <property name="breakAfterAcquireFailure" value="true" /> <property name="testConnectionOnCheckout" value="false" /> </bean>