MySQL连接池“Communications link failure”的问题

当MySQL连接空闲超时,如'wait_timeout'达到28800秒,Mysql会自动断开连接,导致'Communications link failure'错误。解决办法包括启用连接池的校验机制,如testOnBorrow,或者在JDBC URL中添加?autoReconnect=true,以确保连接有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

错误描述

使用MySQL,业务程序需要每天运行一次,然后将结果保存到MySQL。发现每次第一次运行没有问题,到了第二次运行的时候就会报错:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 86,399,655 milliseconds ago.  The last packet sent successfully to the server was 54 milliseconds ago.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1121)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3673)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3562)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4113)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2812)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2761)
    at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1612)
    at org.apache.commons.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:208)
    at org.apache.commons.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:208)
    at com.fb.DB.DBProcess.get(DBProcess.java:30)
    at com.fb.task.PersonalInfoTask$1.run(PersonalInfoTask.java:35)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)
Caused by: java.net.SocketException: Software caused connection abort: recv failed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:170)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)
    at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)
    at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3116)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3573)
    ... 13 more

错误产生原因

Mysql服务器默认的“wait_timeout”是8小时(28800秒),如果一个connection的空闲时间超过8小时,Mysql将自动断开该connection,而连接池由于默认不对连接有效性进行检查,会认为该连接仍是有效的。当应用申请该连接时,就会导致上面的错误。

解决办法

  • 增加有效性检测
    DHCP提供testOnBorrow,testOnReturn, testWhileIdle进行校验,默认都是关闭的,可以选择打开。
    使用DataSource的set函数可以进行设置。
    对应的JAVA代码为:
        ds.setTestOnBorrow(true);
        ds.setTestOnReturn(true);
        ds.setTestWhileIdle(true);
        ds.setValidationQuery("select count(*) from DUAL");

连接池校验机制有2种:
1. 同步验证方式:
每次获取连接或关闭连接时,执行一个预定义的验证语句(sql语句),来验证连接池中的连接是否有效,如果验证失败,彻底关闭此连接。这种方式会导致每次执行数据库操作时都有额外的开销,对性能影响较大。
2. 心跳验证方式:
每隔特定的时间进行一次验证,执行一个预定义的验证语句(sql语句),来验证连接池中的连接是否有效,如果验证失败,彻底关闭此连接。
可以根据具体情况,适当的调节验证间隔时间。这种方式以牺牲较小的性能开销为代价,来保持系统的稳定性。

一般来说不推荐使用同步验证方式,如果应用与数据库之间环境不稳定,推荐使用心跳验证方式;如果环境稳定,而且对数据库I/O性能要求较高时,可以不进行验证。
  • 如果使用的是JDBC,在JDBC URL上添加?autoReconnect=true,如:
jdbc:mysql://10.10.10.10:3306/dbname?autoReconnect=true
  • 如果是在Spring中使用DBCP连接池,在定义datasource增加属性validationQuery和testOnBorrow,如:
<bean id="vrsRankDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${countNew.jdbc.url}" />
    <property name="username" value="${countNew.jdbc.user}" />
    <property name="password" value="${countNew.jdbc.pwd}" />
    <property name="validationQuery" value="SELECT 1" />
    <property name="testOnBorrow" value="true"/>
</bean>
  • 如果是在Spring中使用c3p0连接池,则在定义datasource的时候,添加属性testConnectionOnCheckin和testConnectionOnCheckout,如:
<bean name="cacheCloudDB" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${cache.url}"/>
    <property name="user" value="${cache.user}"/>
    <property name="password" value="${cache.password}"/>
    <property name="initialPoolSize" value="10"/>
    <property name="maxPoolSize" value="${cache.maxPoolSize}"/>
    <property name="testConnectionOnCheckin" value="false"/>
    <property name="testConnectionOnCheckout" value="true"/>
    <property name="preferredTestQuery" value="SELECT 1"/>
</bean>

参考
http://nkcoder.github.io/blog/20140712/mysql-reconnect-packet-lost/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值