记一次网络波动导致druid连接池无法创建新连接的BUG

背景

项目使用了阿里druid jdbc连接池。某天环境出现网络波动,持续20分钟后,网络恢复,单服务一直无法连接数据库。重启之后正常运行

druid 版本:1.1.21

项目配置

spring.datasource.druid.game.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.druid.game.initial-size = 5
spring.datasource.druid.game.min-idle = 5
spring.datasource.druid.game.max-active = 100
spring.datasource.druid.game.max-wait = 30000
spring.datasource.druid.game.time-between-eviction-runs-millis = 60000
spring.datasource.druid.game.min-evictable-idle-time-millis = 300000
spring.datasource.druid.game.validationQuery = SELECT 'x'
spring.datasource.druid.game.test-while-idle = true
spring.datasource.druid.game.test-on-borrow = false
spring.datasource.druid.game.test-on-return = false
spring.datasource.druid.game.poolPreparedStatements = true
spring.datasource.druid.game.maxPoolPreparedStatementPerConnectionSize = 20
spring.datasource.druid.game.maxOpenPreparedStatements = 20

Druid的Bug现场

翻看当时日志,发现大量CannotGetJdbcConnectionException异常日:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:

### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 1, createElapseMillis 686180, createErrorCount 3

Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 1, createElapseMillis 686180, createErrorCount 3
### The error may exist in class path resource [mapper/SysDictMapper.xml]
### The error may involve com.stnts.suileyoo.game.api.dao.SysDictDao.select
### The error occurred while executing a query
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 1, createElapseMillis 686180, createErrorCount 3
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:149)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76)
    at sun.reflect.GeneratedMethodAccessor135.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426)
    ... 95 common frames omitted
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 1, createElapseMillis 686180, createErrorCount 3
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:82)
    at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:80)
    at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67)
    at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:336)
    at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:86)
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:62)
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324)
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:83)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
    ... 101 common frames omitted
Caused by: com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 1, createElapseMillis 686180, createErrorCount 3
    at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1722)
    at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1402)
    at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1382)
    at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1372)
    at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:109)
    at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:158)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:116)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:79)
    ... 111 common frames omitted

Bug定位

系统是通过Druid连接

获取连接超时(GetConnectionTimeoutException)此错误的出现,只有两种可能:

1.业务系统本身Druid获取连接失败。    
2.网络有问题或者数据库不可用。         

在这个Bug里面很明显是Druid创建连接失败,原因如下:

1.之前网络的确有问题,单已恢复,另外还有另外一个服务也是访问这个数据库的。但是那个访问正常。    
2.重启服务之后一切正常。  

如果说还是网络连接异常,那么并不能解释重启后正常这一现象。

Druid获取连接的过程

在分析这个问题之前,先得看下Druid是如何创建连接的,下面是本人在上网查找到的,示意图:

(备注:来源 https://my.oschina.net/alchemystar/blog/899987)
可见druid创建连接都是通过一个专门的线程来进行的,此图省略了大量的源码细节。

然后对比DruidDataSource源码,验证了的确是上图所示的流程。

定位源码

根据日志“

Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 1, createElapseMillis 686180, createErrorCount 3

 ”,发现日志打印位置,如下图:

holder == null 是关键。然后查找holder是如果被赋值的,经查找源码,发现其如下图:

 

 因为已经配置过maxWait属性,故定位到pollLast方法中,再结合druid连接流程,找到CreateConnectionTask的runInternal方法。仔细阅读发现如下逻辑

 经过比对日志输出,最终发现创建连接时,没有创建就直接返回了。真相了,原来是未设置keepAlive属性导致的。。。。。泪奔

起伏

已经定位到了问题,但是在查找资料的过程中,发现有人说在1.1.21版本中配置了keepAlive也是出现类似的情况

详见https://github.com/alibaba/druid/issues/3889

后查找druid发版日志,找到如下日志:

 随将druid版本到1.1.24版本,但是发现日志中会频繁出现“discard long time none received connection”的错误日志。然后进一步查找相关问题,找到下面的一篇文章https://gitee.com/y_project/RuoYi-Vue/issues/I1XWZE?_from=gitee_search

 此错误不影响链接数据库,去除需要设置System.setProperty("druid.mysql.usePingMethod", "false");  因为采用的spring-boot,随将这行代码加入到Application的main方法中,经测试之后,已无此错误日志频繁出现的问题。

解决 

最终,升级druid版本到1.1.24版本,另外在配置中增加了keepAlive属性,并在Application的main方法中加入System.setProperty("druid.mysql.usePingMethod", "false");

题外

不配置maxWait可能带了的BUG https://zhuanlan.zhihu.com/p/144446516

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Druid连接池可以用于连接和管理ClickHouse数据库。ClickHouse是一个面向在线分析处理(OLAP)的列式数据库管理系统,支持高速的查询和分析大规模数据。 使用Druid连接池可以提高连接的效率和性能。连接池是一种连接管理机制,通过创建和维护一组预先初始化的数据库连接,应用程序可以通过从连接池获取连接来进行数据库访问操作,而不需要频繁地创建和销毁数据库连接Druid连接池支持连接的复用和管理,可以有效地减少连接创建和销毁开销,提高数据库操作的效率。连接池可以通过设置最大连接数和最小空闲连接数,自动管理连接的数量,并根据需要动态调整连接创建和销毁。 在使用Druid连接池连接ClickHouse时,需要先配置连接池的参数,如连接URL、用户名、密码等。然后可以通过从连接池获取连接来进行数据库操作,如执行查询、插入、更等操作。使用完毕后,需要通过释放连接的方式将连接返回给连接池,以便其他应用程序可以重用该连接Druid连接池还提供了一些额外的特性,如连接的健康检查、连接的超时控制、连接的统计信息等,可以帮助我们更好地管理和监控连接的状态和性能。 总而言之,通过使用Druid连接池连接ClickHouse数据库,可以提高连接的效率和性能,减少连接创建和销毁的开销,并且提供一些额外的特性来管理和监控连接的状态和性能。 ### 回答2: Druid连接池与ClickHouse的结合为企业提供了强大的数据分析和查询功能。Druid是一个用于实时大数据分析的开源分布式数据存储系统,而ClickHouse是一个列式数据库管理系统,专注于高性能的数据存储和查询。 首先,Druid连接池可以通过将ClickHouse作为数据源来实现对ClickHouse数据的访问和查询。通过配置Druid的数据源连接信息,可以将ClickHouse集群加入到Druid连接池中。这样一来,Druid可以将ClickHouse集群中的数据加载到内存中进行高速查询和分析。 Druid连接池与ClickHouse的结合具有以下几个优点。首先,Druid的分段存储机制使得大部分数据都可以在内存中进行操作,因此查询速度非常快。同时,ClickHouse的列式存储方式可以有效地压缩数据,减小存储空间的占用。这样一来,企业可以实现高速的实时数据分析,并且节约存储空间。 其次,Druid与ClickHouse相结合可以实现多维分析。Druid连接池可以将ClickHouse中的数据按照多个维度进行分片和聚合,提供多维度的数据分析功能。企业可以基于Druid连接池对ClickHouse数据进行灵活的查询和分析,获取深入的业务洞察。 最后,Druid连接池与ClickHouse的结合还可以支持高可扩展性。Druid连接池可以通过与ClickHouse集群的横向扩展来处理大规模的数据查询和分析需求。ClickHouse通过水平扩展节点来增加查询和存储的吞吐量,而Druid连接池可以无缝地与扩容的ClickHouse集群进行对接,满足企业日益增长的数据处理需求。 综上所述,Druid连接池与ClickHouse的结合为企业提供了高速、多维的数据分析和查询功能,并支持高可扩展性。这种结合可以帮助企业更好地利用和分析大数据,为业务决策提供准确的依据。 ### 回答3: Druid是一个实时分析大数据的开源项目,而ClickHouse是一个快速的列式存储分析数据库。连接Druid和ClickHouse可以将实时数据分析与大数据存储和查询相结合,从而实现更高效的数据分析和查询。 Druid连接池是一个用于管理Druid连接的工具,它可以提供一个连接池来保存和管理与ClickHouse的连接连接池的主要作用是优化连接创建和销毁过程,减少连接创建和销毁开销,从而提高连接的复用率和效率。 使用Druid连接池连接ClickHouse的步骤如下: 1. 配置Druid连接池:首先,我们需要在Druid的配置文件中设置ClickHouse的连接参数,包括ClickHouse的IP地址、端口、用户名和密码等信息。这样Druid连接池才能正确地建立与ClickHouse的连接。 2. 创建连接:接下来,我们可以通过Druid连接池创建与ClickHouse的连接连接池会根据配置文件中设置的参数来创建连接,同时保持连接的可用性和安全性。 3. 使用连接:一旦连接成功建立,我们就可以使用连接来执行各种ClickHouse的操作,例如查询数据、写入数据等。连接池会自动管理连接的生命周期,包括连接的获取、释放和回收等操作。 4. 释放连接:当我们使用完连接后,应该及时释放连接,以便让连接进入连接池进行重复利用。这样可以避免频繁地创建和销毁连接,提高连接的复用率和效率。 连接池能够有效地管理连接资源,提高连接的利用率。通过连接池连接Druid和ClickHouse,我们可以实现高效的实时数据分析和查询,提升数据处理和分析的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值