问题
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 4,514,485 milliseconds ago. The last packet sent successfully to the server was 3 milliseconds ago.
现象说明
按字面意思:Mysql的连接异常,最近一次从mysql获取到数据包是在4,514,485毫秒之前,而最近一次给mysql发送数据包是在 3 毫秒前。
根因分析
这个与mysql的连接被持有了4,514,485毫秒,这段持有时间内没有任何任何的交互,mysql连接一直处于等待状态,mysql有个等待超时的设置,查下:
show variables like 'wait_timeout';
发现wait_timeout这个等待超时时间小于这个mysql连接的持有时间。mysql连接因等待超时,mysql服务器会主动关闭该连接。而客户端并不知晓,认为连接依然有效,并试图发送请求,这时就会抛出这个连接异常信息。
解决方法
方法1
修改mysql服务器配置 wait_timeout,使其足够大
方法2
修改mysql客户端连接的参数配置。客户端连接的url参数建议加上:autoReconnect=true&failOverReadOnly=false
高可用的mysql url参数配置如下表所示:
参数名 | 参数说明 | 缺省值 | 最低版本要求 |
---|---|---|---|
autoReconnect | 当数据库连接异常中断时,驱动是否自动重新连接?如果启用,那驱动在抛出连接异常后,在下一个query前会重建好连接,但副作用就是可能会破坏会话状态和数据一致性,除非程序设计时对这种异常有合适的处理。Mysql官方建议直接修改服务器的wait_timeout配置。 | false | 1.1 |
autoReconnectForPools | 是否使用针对数据库连接池的重连策略 | false | 3.1.3 |
failOverReadOnly | 自动重连后连接是否设置为‘只读’模式 | true | 3.0.12 |
maxReconnects | autoReconnect为true时,尝试重试连接的最大次数 | 3 | 1.1 |
reconnectAtTxEnd | autoReconnect为true时,驱动是否每次事务后尝试重连 | false | 3.0.10 |
initialTimeout | autoReconnect为true时,两次尝试重连的时间间隔 | 2 | 1.1 |
failOverReadOnly=false是为了避免在重连后,有数据更新或插入操作在‘read-only’连接中无法执行而抛出新的异常
方法3
在理解方法2的基础上,如果应用了hibernate,则需要配置如下:
<property name="connection.autoReconnect">true</property>
<property name="connection.autoReconnectForPools">true</property>
<property name="connection.is-connection-validation-required">true</property>
如果还是用C3P0连接池框架的话,配置如下
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">0</property>
<property name="hibernate.c3p0.timeout">0</property>
<property name="hibernate.c3p0.validate">true</property>
另外,客户端连接中也有一个的超时时间,这个超时时间的配置一定要小于服务端的超时时间配置。
###方法4
优化程序代码,捕获超时异常,进行异常处理。如果方法2和方法3不起作用,那最好的方式就是优化代码,避免在一个连接中执行耗时的操作。