Could not open JDBC Connection for transaction; nested exception is : Communications link failure

CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 2,297,746 milliseconds ago.  The last packet sent successfully to the server was 4 milliseconds ago.

什么,创建事务失败,内部异常 连接失败?what?

通过查找资料,可能是wait_timeout设置过小导致的,尝试加大wait_timeout的值后,系统恢复正常。或者修改连接池的配置信息。

那么wait_timeout是什么?

wait_timeout:当数据库重启数据库空闲连接超过设置的最大timemout时间,数据库会强行断开已有的链接,MySQL服务器默认的“wait_timeout”默认是28800秒即8小时,意味着如果一个连接的空闲时间超过8个小时,MySQL将自动断开该连接,而连接池却认为该连接还是有效的(因为并未校验连接的有效性),当应用申请使用该连接时,就会导致上面的报错。

还有个interactive_timeout 参数针对交互式连接。wait_timeout针对非交互式连接。所谓的交互式连接,即在mysql_real_connect()函数中使用了CLIENT_INTERACTIVE选项。

     说得直白一点,通过mysql客户端连接数据库是交互式连接,通过jdbc连接数据库是非交互式连接。 

在连接启动的时候,根据连接的类型,来确认会话变量wait_timeout的值是继承于全局变量wait_timeout,还是interactive_timeout。

所以更改了数据库设置信息,也需要重启应用来使连接信息生效。

Mysql 服务端解决方案

直接修改相关配置信息

##查看MySQL配置

show global variables like 'wait_timeout'; 

 

##先修改全局的值为31536000,重启后将恢复原值

set global wait_timeout=31536000;

 

##修改my.cnf,保证MySQL重启后依然生效

修改my.cnf: 

[mysqld]  

wait_timeout=31536000  

interactive_timeout=31536000   #该信息可以不修改

将过期时间修改为1年。

客户端解决该问题方案

为了解决这个异常,我们在配置数据库连接池的时候需要做一些检查连接有效性的配置,这里以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,即申请连接的时候先试一试连接是否可用,不过带来的影响就是性能降低,需要根据实际需求合理取舍。

猜你喜欢

转载自blog.csdn.net/wangxuelei036/article/details/107840007