问题

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不起作用,那最好的方式就是优化代码,避免在一个连接中执行耗时的操作。