记一次 Druid 超时配置的问题 → 引发对 Druid 时间配置项的探究(druid执行sql超时时间)

  本篇文章为你整理了记一次 Druid 超时配置的问题 → 引发对 Druid 时间配置项的探究(druid执行sql超时时间)的详细内容,包含有druid timeout druid执行sql超时时间 druid querytimeout druid not full timeout retry 记一次 Druid 超时配置的问题 → 引发对 Druid 时间配置项的探究,希望能帮助你了解 记一次 Druid 超时配置的问题 → 引发对 Druid 时间配置项的探究。

   一天在路边看到一个街头采访

   记者:请问,假如你儿子娶媳妇,给多少彩礼合适呢

   大爷:一百万吧,再给一套房,一辆车

   大爷沉思一下,继续说到:如果有能力的话再给老丈人配一辆车,毕竟他把女儿养这么大也不容易

   记者:那你儿子多大了?

   大爷:我没有儿子,有两个女儿

   最近生产环境出现了一个问题,错误日志类似如下

  

Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 1010, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user

 

   日志信息提示的很明显:获取JDBC Connection失败,因为从druid连接池获取connection超时了

   上图的意思是:执行select * from tbl_user之前,需要从druid连接池中获取一个connect

   而此时连接池的状态是:一共 10 个激活的connect,连接池最大创建 10 个connect,正在执行 sql 的connect也是 10 个

   所以不能创建新的connect,那就等呗,一共等了1010毫秒,还是拿不到connect,就抛出GetConnectionTimeoutException异常

   简单点说就是是连接池中连接数不够,在规定的时间内拿不到connect

   那有人就说了:连接池的最大数量设置大一点,问题不就解决了吗

   最大连接数设置大一点只能说可以降低问题发生的概率,不能完全杜绝,因为网络情况、硬件资源的使用情况等等都是不稳定因素

   今天要讲的不是连接池大小问题,而是超时设置问题,我们慢慢往下看

   我们先来模拟下上述问题

   MySQL版本:5.7.21,隔离级别:RR

   Druid版本:1.1.12

   spring-jdbc版本:5.2.3.RELEASE

   DruidDataSource 初始化

   为了方便演示,就手动初始化了

   多线程查询

   线程数多于连接池中connect数

   模拟慢查询

   如果查询飞快,15 个查询,可能都用不上 10 个connect,所以我们需要简单处理下

   很简单,给表加写锁呗:LOCK TABLES tbl_user WRITE

   给表tbl_user加上写锁,然后跑线程去查询tbl_user的数据

   异常演示

   先锁表,再启动程序

   可以看到,15 个线程中,有 5 个线程获取connect失败

  

Thread-13 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user

 

  Thread-5 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user

  Thread-10 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user

  Thread-7 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user

  Thread-8 Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 10004, active 10, maxActive 10, creating 0, runningSqlCount 10 : select * from tbl_user

 

   示例代码:druid-timeout

  时间配置项

   Druid中关于时间的配置项有很多,我们我们重点来看下如下几个

   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

   执行一个事务的超时时间,单位是秒

   minEvictableIdleTimeMillis

   最小空闲时间,单位是毫秒,默认 30 分钟

   如果连接池中非运行中的连接数大于minIdle,并且某些连接的非运行时间大于minEvictableIdleTimeMillis,则连接池会将这部分连接设置成Idle状态并关闭

   maxEvictableIdleTimeMillis

   最大空闲时间,单位是毫秒,默认 7 小时

   如果minIdle设置的比较大,连接池中的空闲连接数一直没有超过minIdle,那么那些空闲连接是不是一直不用关闭?

   当然不是,如果连接太久没用,数据库也会把它关闭(MySQL 默认 8 小时),这时如果连接池不把这条连接关闭,程序就会拿到一条已经被数据库关闭的连接

   为了避免这种情况,Druid会判断池中的连接,如果非运行时间大于maxEvictableIdleTimeMillis,也会强行把它关闭,而不用判断空闲连接数是否小于minIdle

   其实前面的示例中设置了

   获取connect的最大等待时长是10000毫秒,也就是10秒

   而removeAbandonedTimeout设置是 7 秒

   照理来说connect如果 7 秒未执行完SQL查询,就会被Druid强制回收进连接池,那么等待10秒应该能够获取到connect,为什么会抛出GetConnectionTimeoutException异常了?

   这也就是文章标题中的超时设置问题

   很显然,我们从dataSource.init();开始跟源码

   会看到如下一块代码

   我们继续跟createAndStartDestroyThread();

   重点来了,我们看下DestroyTask到底是怎么样一个逻辑

   我们接着跟进removeAbandoned,关键代码

   如果connect正在运行中是不会被强制回收进连接池的

   回到我们的示例,connect都是在运行中,只是都在进行慢查询,所以是无法被强制回收进连接池的,那么其他线程自然在maxWait时间内无法获取到connect

   至此文章标题中的问题的原因就找到了

   那么问题又来了:removeAbandonedTimeout作用在哪?

   我们再仔细阅读下:连接泄漏监测

   Druid提供了RemoveAbandanded相关配置,目的是监测连接泄露,回收那些长时间游离在连接池之外的空闲connect

   可能因为程序问题,导致申请的connect在处理完sql查询后,不能回到连接池的怀抱,那么这个connect处理游离态,它真实存在,但后续谁也申请不到它,这就是连接泄露

   而removeAbandoned的设计就是为了帮助这些泄露的connect回到连接池的怀抱

   开启removeAbandoned对性能有影响,官方不建议在生产环境使用

   那么我们接受官方的建议,不开启removeAbandoned(不配置即可,默认是关闭的)

   为了不让慢查询占用整个连接池,而拖垮整个应用,我们设置查询超时时间queryTimeout

   有两种方式,一个是设置DataSource的queryTimeout,另一个是设置JdbcTemplate的queryTimeout

   如果两个都设置,最终生效的是哪个,为什么?大家自己去分析,权当是给大家留个一个作业

   这里就配置DataSource的queryTimeout,给大家演示下效果

   可以看到,所有线程都获取到了connect

   1、Druid的removeAbandoned对性能有影响,不建议开启

   removeAbandoned的开启后的作用要捋清楚,而非简单的过期强制回收

   2、Druid的时间配置项有很多,不局限于文中所讲,但常用的就那么几个,其他的保持默认值就好

   配置的时候一定要弄清楚各个配置项的具体作业,不要去猜!

   3、查询超时queryTimeout即可在DataSource配置,也可在JdbcTemplate配置

  以上就是记一次 Druid 超时配置的问题 → 引发对 Druid 时间配置项的探究(druid执行sql超时时间)的详细内容,想要了解更多 记一次 Druid 超时配置的问题 → 引发对 Druid 时间配置项的探究的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: