5

彻底解决jdbc数据库连接超时重试-communication link failure的正确姿势

 3 years ago
source link: https://blog.csdn.net/lifetragedy/article/details/116641291
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

必须确保你的druid jdbc版本为1.2.6

这个问题只有在druid 1.2.6里解决,因为只要是低于druid 1.2.6版本,本身就存在bug,无论你怎么设都会打断连接。

我们经常会在日志中看到“jdbc connection timeout, last connection was 11,080 ms这样的错误。

这个代表MYSQL主动把你的jdbc连接给踢掉了。

为什么MYSQL要踢掉connection?

这个很正常,那是因为:MYSQL不可能无限接在接受一个数据库连接请求后就一直给你KEEP在那边的。生产环境都是这么一个“梗”,试想一个connection里的一个sql的迪卡尔积有10mb,2000个边接keep在那8小时不关闭,这个mysql会被随时打爆。

在2001年古老的oracle 7g、8i时代,就已经有一个参数叫timeout,根据oracle创始人Larry Ellison和“杀死比尔”-哦,错了,是比尔.盖茨对这个参数解释是:1分钟。即数据库keep住你的一个连接1分钟,这1分钟内如果没有任何的重复连接那么该连接会被数据库踢掉。

这就是生产数据库的“自我保护”机制,这是必须的。这也是为什么我强调大家你的“线上SQL单条执行必须<1s“的道理,因为你试想一下在国内的场景,动不动一秒内来1000个并发?是不是这样的?

MYSQL的默认超时是8小时,这个值肯定是不能接受的。在奥乐齐这边我们考虑到实际业务场景和开发成熟度,目前这个超时设的是10分钟,即10分钟内这条connection连接过来后没有进一步动作,该connection会被踢除,这个规则已经很“宽容”了。

那么这就要求我们的JAVA应用具有自动重连功能

网上的教程统统都是错的

我可以说100%都是错的,我们在一个企业级的应用中,JDBC连接的参数至少有19个,这19个每一个都是非常重要的,而真正涉及到“自动重连”有三层境界。

自动重连的第一层镜界

确保以下9个参数设置正确,缺一个都无法自动重连

  1. jdbc connection url中需要有:autoReconnect=true&useUnicode=true&characterEncoding=utf-8
  2. minIdle值与initialSize相同
  3. maxIdle最大空闲值与initalSize和minIdle相同
  4. testOnBorrow设为false
  5. testOnReturn设为true
  6. testWhileIdle设为true
  7. timeBetweenEvictionRunsMillis设为480000 #代表8分钟,我们生产上的的mysql的timeout为10分钟,因此这个值必须<生产上的mysql的timeout的值
  8. numTestsPerEvictionRun设为10 #默认为3,这个值在common dbcb或者是非druid connection pool的情况下必须按照数据库内设置的timeout和每隔多少个timeBetweenEvictionRunsMillis以及initalSize来计算的,否则会发生在若干时间(1小时后)全部连接超时后,而initSize由于>此值,因此导致(initSize-numTestsPerEvictionRun)无效,进而导致系统上在获取到无效连接时抛错。举例来说数据库的timeout值为10分钟,initialSize为50个,timeBetweenEvictionRunsMillis为5分钟,那么此时这个值你要设为(timeBetweenEvictionRunsMillis/数据库timeout)*initialSize+1个,在此例中=(8/10)*50+1=41个,为了保险期间,你可以把这个值在此例中设为:45个
  9. minEvictableIdleTimeMillis设为480000 #代表8分钟,我们生产上的的mysql的timeout为10分钟,因此这个值必须<生产上的mysql的timeout的值

自动重连的第二层镜界

假设我们以上9个参数全部设置了,可是还有一个问题。

假设我们的连接中有一个“长事务”,这个事务一直在运行,你不能硬生生用mysql里设置的timeout 10分钟去硬生生打断正在运行中的事务吧?此时我们要赋于我们的jdbc connection pool一个自动续约的的功能,怎么设这个自动续约呢?

  • maxEvictableIdleTimeMillis: 600000 #等于数据失效时间
  • keepAlive: true
  • keepAliveBetweenTimeMillis: 540000 #保活时间即<数据库失效时间

此处需要高度注意的是:APACHE的DBCP数据库连接池是不支持keepalive这个参数的。没有这个参数其它两个参数都设了无效,这也是为什么我们后期准备把apache dbcp改成阿里的druid。

自动重连的第三层镜界

你以为把上面这些东西我们一股脑的都设置进去后就可以了?如下面这样

错!还不够!

这只是配置,还没有让代码生效呢。怎么让代码生效?你得写spring boot 的自动装配类

到此,三重境界经历过后整个JAVA应用的jdbc才具有了真正的“自动重连”功能。

骚年,程序员不好做哈!

附:核心19个参数设置全解释

  1. jdbc connection url中需要有:autoReconnect=true&useUnicode=true&characterEncoding=utf-8
  2. minIdle值与initialSize相同
  3. maxIdle最大空闲值与initalSize和minIdle相同
  4. maxWait连接超时统一可以设成=5000(5秒),正式生产环境调优的好一般是设成1秒的,这就是为什么生产上高于1秒的sql必须要优化的道理
  5. minEvictableIdleTimeMillis: 480000 #小于mysql maxinteractive的值(奥乐齐生产这边是10分钟)

  6. maxEvictableIdleTimeMillis: 600000 #必须于mysql maxinteractive的值(奥乐齐生产这边是10分钟)
  7. keepAliveBetweenTimeMillis: 540000 #必须小于mysql maxinteractive的值(奥乐齐生产这边是10分钟)
  8. testOnBorrow设为false
  9. testOnReturn设为true
  10. keepAlive:设为true
  11. testWhileIdle设为true
  12. validationQuery设为select 1
  13. validationQueryTimeout设为1
  14. validationInterval设为3000
  15. conRetryTime设为3
  16. dynamicPropertiesSupport设为false #这个值是什么意思, 欧电对若干mybatis用tk.mabatis.mapper中的dynamic进行了封装,如果你的pool内的dao层有用到extend BaseMapperProvider(这个是欧电底层包装过的),这个值就必须设成false,一定注意
  17. timeBetweenEvictionRunsMillis设为480000 #代表8分钟,我们的DBwait timeout都设的是8分钟
  18. numTestsPerEvictionRun设为10 #默认为3,这个值在common dbcb或者是非druid connection pool的情况下必须按照数据库内设置的timeout和每隔多少个timeBetweenEvictionRunsMillis以及initalSize来计算的,否则会发生在若干时间(1小时后)全部连接超时后,而initSize由于>此值,因此导致(initSize-numTestsPerEvictionRun)无效,进而导致系统上在获取到无效连接时抛错。举例来说数据库的timeout值为10分钟,initialSize为50个,timeBetweenEvictionRunsMillis为5分钟,那么此时这个值你要设为(timeBetweenEvictionRunsMillis/数据库timeout)*initialSize+1个,在此例中=(8/10)*50+1=41个,为了保险期间,你可以把这个值在此例中设为:45个
  19. minEvictableIdleTimeMillis=480000 #代表8分钟,我们的DBwait timeout都设的是8分钟,这个值必须等于timeBetweenEvictionRunsMillis值,如果不设这个值在使用common dbcp的pool里照样不能实现错误重连

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK