很長的報錯,截取
-
ERROR c.a.d.p.DruidDataSource - discard connection
-
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
-
-
The last packet successfully received from the server was 44,866 milliseconds ago. The last packet sent successfully to the server was 0 milliseconds ago.
-
at sun.reflect.GeneratedConstructorAccessor103.newInstance(Unknown Source) ~[na:na]
-
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java: 45) ~[na:1.7.0_79]
-
at java.lang.reflect.Constructor.newInstance(Constructor.java: 526) ~[na:1.7.0_79]
-
at com.mysql.jdbc.Util.handleNewInstance(Util.java: 404) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java: 981) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java: 3465) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java: 3365) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java: 3805) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java: 2478) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java: 2625) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java: 2551) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java: 1861) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java: 1962) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeQuery(DruidPooledPreparedStatement.java: 227) ~[druid-1.0.5.jar:1.0.5]
-
at com.jfinal.plugin.activerecord.DbPro.query(DbPro.java: 73) [jfinal-2.2.jar:na]
-
at com.jfinal.plugin.activerecord.DbPro.query(DbPro.java: 100) [jfinal-2.2.jar:na]
-
at com.jfinal.plugin.activerecord.Db.query(Db.java: 47) [jfinal-2.2.jar:na]
-
...
-
-
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
-
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java: 2957) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java: 3375) ~[mysql-connector-java-5.1.38.jar:5.1.38]
-
... 55 common frames omitted
參考鏈接:
http://blog.csdn.net/pandajava/article/details/41946251
http://blog.csdn.net/shiqidide/article/details/7642531
https://stackoverflow.com/questions/2983248/com-mysql-jdbc-exceptions-jdbc4-communicationsexception-communications-link-fai
起因和現象:
項目運行后,莫名其妙就在log中看到這個報錯,但是數據也能獲取到,不影響日常使用,但是看到了總歸不爽。
看了下網上的原因:
MySQL服務器默認的“wait_timeout”是28800秒即8小時,意味着如果一個連接的空閑時間超過8個小時,MySQL將自動斷開該連接,而連接池卻認為該連接還是有效的(因為並未校驗連接的有效性),當應用申請使用該連接時,就會導致上面的報錯。
出現異常”The last packet sent successfully to the server was 0 milliseconds ago.“的大部分原因
是由於數據庫回收了連接,而系統的緩沖池不知道,繼續使用被回收的連接所致的。
解決方法:
1.JDBC的URL中加上屬性(舊版本可用,不推薦)
按照錯誤的提示,可以在JDBC URL中使用autoReconnect屬性,實際測試時使用了autoReconnect=true&failOverReadOnly=false,不過並未起作用,
使用的是5.1版本,可能真像網上所說的只對4之前的版本有效。
2.修改MYSQL的配置文件my.ini/my.cnf,添加超時等待參數的最長時間(推薦)
沒辦法,只能修改MySQL的參數了,wait_timeout最大為31536000即1年,在my.cnf中加入:
[mysqld]
wait_timeout=86400 (1天)
interactive_timeout=7200
重啟生效,需要同時修改這兩個參數。
3.修改配置,讓緩沖池驗證鏈接是否有效
#SQL查詢,用來驗證從連接池取出的連接
dbcp.validationQuery=SELECT 1
#指明連接是否被空閑連接回收器(如果有)進行檢驗,如果檢測失敗,則連接將被從池中去除
dbcp.testWhileIdle=true
#在空閑連接回收器線程運行期間休眠的時間值,以毫秒為單位,一般比minEvictableIdleTimeMillis小
dbcp.timeBetweenEvictionRunsMillis=300000
#在每次空閑連接回收器線程(如果有)運行時檢查的連接數量,最好和maxActive一致
dbcp.numTestsPerEvictionRun=50
#連接池中連接,在時間段內一直空閑,被逐出連接池的時間(1000*60*60),以毫秒為單位
dbcp.minEvictableIdleTimeMillis=3600000
如果在wait_timeout秒期間內,數據庫連接(java.sql.Connection)一直處於等待狀態,mysql5就將該連接關閉。這時,你的Java應用的連接池仍然合法地持有該連接的引用。當用該連接來進行數據庫操作時,就碰到上述錯誤。這解釋了為什么我的程序第二天不能登錄 的問題。
你可能會想到在tomcat的數據源配置中有沒有辦法解決?的確,在jdbc連接url的配置中,你可以附上“autoReconnect=true”,但這僅對mysql5以前的版本起作用。增加“validation query”似乎也無濟於事。
原因分析
當數據庫重啟或數據庫空閑連接超過設置的最大timemout時間,數據庫會強行斷開已有的鏈接,最大timeout時間可以通過命令show global variables like "wait_timeout";
查詢:
mysql> show global variables like "wait_timeout"; +---------------+-------+ | VARIABLE_NAME | VALUE | +---------------+-------+ | wait_timeout | 28800 | +---------------+-------+ 1 row in set (0.00 sec)
解決辦法
為了解決這個異常,我們在配置數據庫連接池的時候需要做一些檢查連接有效性的配置,這里以Druid為例,相關配置如下(更多配置):
字段名 | 默認值 | 說明 |
---|---|---|
validationQuery | 用來檢測連接是否有效的sql,要求是一個查詢語句,常用select 'x'。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會起作用。 | |
validationQueryTimeout | 單位:秒,檢測連接是否有效的超時時間。底層調用jdbc Statement對象的void setQueryTimeout(int seconds)方法 | |
testOnBorrow | true | 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。 |
testOnReturn | false | 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。 |
testWhileIdle | false | 建議配置為true,不影響性能,並且保證安全性。申請連接的時候檢測,如果空閑時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。 |
timeBetweenEvictionRunsMillis | 1分鍾(1.0.14) | 有兩個含義:1) Destroy線程會檢測連接的間隔時間,如果連接空閑時間大於等於minEvictableIdleTimeMillis則關閉物理連接。2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明 |
為了避免空閑時間過長超過最大空閑時間而被斷開,我們設置三個配置:
validationQuery: SELECT 1 testWhileIdle: true timeBetweenEvictionRunsMillis: 28000
其中timeBetweenEvictionRunsMillis
需要小於mysql的wait_timeout
。
但是這種方法無法避免重啟的情況,不過一般數據庫不會頻繁重啟,影響不大,如果非得頻繁重啟,可以通過設置testOnBorrow
,即申請連接的時候先試一試連接是否可用,不過帶來的影響就是性能降低,需要根據實際需求合理取舍。
轉自:
https://blog.csdn.net/tuntun1120/article/details/78866991