一.前言:
完整报错如下:
main com.alibaba.druid.pool.DruidPooledStatement CommunicationsException, druid version 1.2.16, jdbcUrl : jdbc:mysql://XXX/XXX?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false, testWhileIdle true, idle millis 10097, minIdle 5, poolingCount 5, timeBetweenEvictionRunsMillis 60000, lastValidIdleMillis 10097, driver com.mysql.cj.jdbc.Driver, exceptionSorter com.alibaba.druid.pool.vendor.MySqlExceptionSorter
2025-04-09 16:45:59.463 ERROR [mainraceId] main com.alibaba.druid.pool.DruidDataSource {conn-10004} discard
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet successfully received from the server was 10,059 milliseconds ago. The last packet sent successfully to the server was 10,061 milliseconds ago.
...
at org.XXX.java:58)
在网上搜查过相关资料,排除了如下问题:
1.网络问题 :通过telnet命令排除
2.数据库地址、数据库密码不正确 :通过本地测试连接排除
3.mysql8驱动的兼容问题:查询相关资料排除
最后发现问题是不小心引入了durid的jar包(从报错里的druid version 1.2.16也可以猜到部分原因),而durid的相关默认配置可能导致连接超时。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
</dependency>
二.解决方案
1.去掉durid的maven配置
2.增加或调整durid的相关配置
(1)durid的默认配置
查看的时候注意自己使用的版本,官网是以较新版本1.2.22为准。
参考官方文档:https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
(2)yml配置示例
spring:
#数据库配置
datasource:
#这里使用的是德鲁伊数据库连接池
druid:
db-type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url:
username:
password:
#初始连接数量
initial-size: 20
#最小连接数量
min-idle: 20
#最大连接数量
max-active: 1000
#获取连接等待超时的时间 单位是毫秒,这里配置60秒
max-wait: 60000
#查询超时时间 单位是毫秒 这里配置6秒,如果你查询数据量大的情况下这个配置的大一点,不然查询报错抛出异常
query-timeout: 6000
#事务发生回滚后多长时间响应用户单位是毫秒,这里这个时间和mysql的innodb_lock_wait_timeout是相加的关系,如果你在mysql配置文件配置了这个时间,那时间就会相加
transaction-query-timeout: 2000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位毫秒, 这里配置10分钟去检测一次是否存在空闲连接
time-between-eviction-runs-millis: 600000
#最大等待线程数量 这里配置50,当你的连接满了之后,其他的连接进入等待池子,最大的数量为50,超过这个数量的线程舍弃掉
max-wait-thread-count: 50
#最小连接时间,也就是说一条线程的空闲时间超过了这个数就会被关闭,这里的单位是毫秒,这里设置为10分钟
min-evictable-idle-time-millis: 600000
#最大连接时间,也就是说一条线程的空闲时间>最小连接时间,也>最大连接时间,直接关闭 这里设置为30分钟
max-evictable-idle-time-millis: 1800000
#申请连接时直接检测连接是否有效
test-on-borrow: true
#申请连接时检测空闲时间,根据空闲时间再检测连接是否有效
test-while-idle: true
#归还连接时检测连接是否有效,这个配置生产最好不要配置设置为ture,有性能问题
test-on-return: true
#初始连接失败是否抛出异常
init-exception-throw: true
#开启检查运行的状态
check-execute-time: true
#这里是健康检查sql语句,配合下面的使用
validation-query: SELECT 1 FROM DUAL
#多长时间进行一次心跳去检查是否连接了mysql,这里配置为30分钟
validation-query-timeout: 1800000
#连接出错后在尝试3次
connection-error-retry-attempts: 3
#数据库宕机后启用重连机制,true表示失败后立即断开,false表示失败后进行断线重连尝试,配合上面的重连次数
break-after-acquire-failure: false
#重连的间隔 单位毫秒 这里设置30分钟重连
time-between-connect-error-millis: 1800000
#开启保持连接有效性
keep-alive: true
#只有空闲时间大于keep-alive-between-time-millis并且小于min-evictable-idle-time-millis该参数才会有用,单位为毫秒 这里设置为15分钟
#这个时间必须大于检测时间time-between-eviction-runs-millis
keep-alive-between-time-millis: 900000
(3)时间相关配置项说明
配置项 | 含义 | 实现原理 |
---|---|---|
maxWait | 最大等待时长,单位是毫秒,-1 表示无限制 | 从连接池获取 connect ,如果有空闲的 connect ,则直接获取到,如果没有则最长等待 maxWait 毫秒,如果还获取不到,则抛出 GetConnectionTimeoutException 异常 |
removeAbandonedTimeout | 设置 druid 强制回收连接的时限,单位是秒 | 官网说明:连接泄漏监测。从连接池获取到 connect 开始算起,超过此值后, Druid 将强制回收该连接 |
validationQueryTimeout | 检测连接是否有效的超时时间,单位是秒,-1 表示无限制 | Druid 内部的一个检测 connect 是否有效的超时时间,需要结合 validationQuery 来配置 |
timeBetweenEvictionRunsMillis | 检查空闲连接的频率,单位是毫秒, 非正整数表示不进行检查 | 空闲连接检查的间隔时间, Druid 池中的 connect 数量是一个动态从 minIdle 到 maxActive 扩张与收缩的过程。connect 使用高峰期,数量会从 minIdle 扩张到 maxActive ,使用低峰期, connect 数量会从 maxActive 收缩到 minIdle。收缩的过程会回收一些空闲的 connect ,而 timeBetweenEvictionRunsMillis 就是检查空闲连接的间隔时间 |
queryTimeout | 执行查询的超时时间,单位是秒,-1 表示无限制 | 最终会应用到 Statement 对象上,执行时如果超过此时间,则抛出 SQLException |
transactionQueryTimeout | 执行查询的超时时间,单位是秒,-1 表示无限制 | |
minEvictableIdleTimeMillis | 最小空闲时间,单位是毫秒,默认 30 分钟 | 如果连接池中非运行中的连接数大于 minIdle ,并且某些连接的非运行时间大于 minEvictableIdleTimeMillis ,则连接池会将这部分连接设置成 Idle 状态并关闭 |
maxEvictableIdleTimeMillis | 最大空闲时间,单位是毫秒,默认 7 小时 | 如果 minIdle 设置的比较大,连接池中的空闲连接数一直没有超过 minIdle ,那么那些空闲连接是不是一直不用关闭?当然不是,如果连接太久没用,数据库也会把它关闭(MySQL 默认 8 小时),这时如果连接池不把这条连接关闭,程序就会拿到一条已经被数据库关闭的连接。为了避免这种情况, Druid 会判断池中的连接,如果非运行时间大于 maxEvictableIdleTimeMillis ,也会强行把它关闭,而不用判断空闲连接数是否小于 minIdle |
socketTimeout | 新增的控制创建连接时的socket最大读超时,单位是毫秒,默认0表示永远等待,配置成10000则表示db操作如果在10秒内未返回应答,将抛出异常 | 工作原理是在创建连接时将该值设置到对应数据库驱动的属性信息中由其JDBC驱动进行控制 |
(4)报错解析
①从“The last packet successfully received from the server was 10,059 milliseconds ago”可以看出,10秒前有过一次成功连接,但是由于某种原因连接失效了。
②根据“at org.XXX.java:58)”定位到某个sql,直接执行这个sql发现执行时间超过了十秒。则可能是这个sql导致的。
此时我们可以选择优化这个慢sql。或者调整durid超时时间的设置。
③调整durid超时时间socketTimeout的设置,在1.2.12到1.2.21等早期版本默认值为10000,db操作如果在10秒内未返回应答,将抛出异常。调大socketTimeout即可。
: