现象
日志报错:com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
原因
从数据库连接池拿到了已经关闭的连接,导致报错。druid有定时任务进行空闲连接的检测和回收,当连接时长超过mysql的连接超时时间时,会被mysql强制断开,而如果此时数据库连接池并没有检测到连接已断开并交给应用去使用就会导致报错。
1、查看mysql服务器端的连接超时时间,单位秒,默认时8小时
show global variables like 'wait_timeout'
show global variables like 'interactive_timeout'
druid的关键参数详解
minIdle: 最小连接池数量
minEvictableIdleTimeMillis: 单位毫秒,默认30分钟,如果连接池中空闲连接大于minIdle数量,且连接空闲时间超过该值,则进行连接的回收操作。
maxEvictableIdleTimeMillis: 单位毫秒,默认7小时,如果连接空闲时间超过该值则强制关闭。
timeBetweenEvictionRunsMillis: 单位毫秒,两个作用:
1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接,连接时在取一条活跃的连接。
2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
testWhileIdle:默认值为false。 建议配置为true, 不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
validationQuery: 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用
解决方案
当以上参数配置不合理时,一般人错误原因应该是3
1)minEvictableIdleTimeMillis过大 , 空闲连接一直没有被回收,而超过mysql连接超时时间被强制回收后,则此时获取到的连接会导致报错。
2)timeBetweenEvictionRunsMillis配置过大,则在该时间窗口内获取到的连接可能报错。
3)maxEvictableIdleTimeMillis在某种情况下7小时未自动回收,导致8小时后数据库自动断开,建议改成3小时或更小。timeBetweenEvictionRunsMillis改成10000以便10秒检查一遍,testWhileIdle设置true
验证:
数据库超时时间设为30s
set global wait_timeout=30;
1, test-while-idle 为false情况
条件:minEvictableIdleTimeMillis和maxEvictableIdleTimeMillis设置的足够大,test-while-idle设置为false
分析:执行第一次请求建立数据库连接,等待30s后执行第二次请求,此时连接已被数据库断开,因为连接池minEvictableIdleTimeMillis足够大,所以不会关闭空闲连接,且test-while-idle为false不会在请求时检测连接,所以预期会报错
maxActive: 20
initialSize: 1
minIdle: 1
maxWait: 60000
time-between-eviction-runs-millis: 40000
minEvictableIdleTimeMillis: 1800000
maxEvictableIdleTimeMillis: 2400000
validationQuery: SELECT 1
test-while-idle: false
test-on-borrow: false
test-on-return: false
2, test-while-idle true,time-between-eviction-runs-millis<30
条件:minEvictableIdleTimeMillis和maxEvictableIdleTimeMillis设置的足够大;
test-while-idle设置为true,time-between-eviction-runs-millis<30s
分析:执行第一次请求建立数据库连接,等待30s后执行第二次请求,此时连接已被数据库断开,因为连接池minEvictableIdleTimeMillis足够大,所以不会关闭空闲连接,test-while-idle为true会检测空闲超过time-between-eviction-runs-millis的连接,且time-between-eviction-runs-millis<30s, 等待30秒后进行第二次请求时会执行连接有效性检测抛弃无效连接,所以总是能拿到有效连接,不会报错。
maxActive: 20
initialSize: 1
minIdle: 1
maxWait: 60000
time-between-eviction-runs-millis: 20000
minEvictableIdleTimeMillis: 1800000
maxEvictableIdleTimeMillis: 2400000
validationQuery: SELECT 1
test-while-idle: true
可以看到第二次请求druid新建了一条连接而不是使用连接池已有的连接。
3,test-while-idle true,time-between-eviction-runs-millis>30
条件:minEvictableIdleTimeMillis和maxEvictableIdleTimeMillis设置的足够大;
test-while-idle设置为true,time-between-eviction-runs-millis>30s
分析:执行第一次请求建立数据库连接,等待30s以后执行第二次请求,此时连接已被数据库断开,因为连接池minEvictableIdleTimeMillis足够大,所以不会关闭空闲连接,test-while-idle为true会检测空闲超过time-between-eviction-runs-millis的连接,且time-between-eviction-runs-millis>30s, 分以下两种情况
maxActive: 20
initialSize: 1
minIdle: 1
maxWait: 60000
time-between-eviction-runs-millis: 40000
minEvictableIdleTimeMillis: 1800000
maxEvictableIdleTimeMillis: 2400000
validationQuery: SELECT 1
test-while-idle: true
1)等待30秒且小于40秒,由于此时空闲时间<time-between-eviction-runs-millis ,所以获取连接时不会执行有效性质检测,所以依然会报错
2)等待40秒后进行第二次请求时会执行连接有效性检测抛弃无效连接,所以总是能拿到有效连接,不会报错。
4,test-while-idle false,
minEvictableIdleTimeMillis设置为40,minIdle设置为0
maxActive: 20
initialSize: 1
minIdle: 0
maxWait: 60000
time-between-eviction-runs-millis: 1000
minEvictableIdleTimeMillis: 40000
maxEvictableIdleTimeMillis: 2400000
validationQuery: SELECT 1
test-while-idle: true
1)等待40秒执行第二次请求操作,则满足如果连接池中空闲连接大于minIdle数量,且连接空闲时间超过该值,则进行连接的回收操作,预期不会报错。
2)等待35秒执行第二次请求,则不满足空闲时间大于40秒,预期会报错
5,其他情况:长事务,time-between-eviction-runs-millis过大,minEvictableIdleTimeMillis和maxEvictableIdleTimeMillis大于wait_timeout等
------------------------------------------与正文内容无关------------------------------------
如果觉的文章写对各位读者老爷们有帮助的话,麻烦点赞加关注呗!小弟在这拜谢了!
如果您觉得我的文章在某些地方写的不尽人意或者写的不对,从而让你对你人生观产生颠覆(概不负责),需要斧正,麻烦在评论区不吝赐教,作者看到后会根据您的提示查阅文章进行修改,还这世间一个公理一片蓝天