当前位置: 首页 > article >正文

HikariPool连接池报错(Possibly consider using a shorter maxLifetime value)

1、错误信息

com.zaxxer.hikari.pool.PoolBase : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@xxxxxxx (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value

在解决错误之前,先理解下HikariPool的相关配置内容:

1.1、认识HikariPool的结构

包括以下几个部分:

    1. ConcurrentBag & PoolEntry
    1. addConnectionExecutor
    1. houseKeeperTask
    1. HikariProxyConnection
    1. closeConnectionExecutor
1.2、ConcurrentBag

直译就是“连接口袋”,就是放数据库连接的口袋,也就是连接池。

ConcurrentBag有3个保存数据库连接的地方(池):

  1. threadList:是一个ThreadLocal变量,保存当前线程持有的数据库连接。
  2. sharedList:是一个CopyOnWriteArrayList,线程安全的arrayList,数据库连接创建后首先进入sharedList。
  3. handoffQueue:是一个SynchronousQueue,数据库连接创建、进入sharedList后,也会进入handoffQueue等待连接请求。

除此之外,ConcurrentBag还有一个比较重要的属性waiters,记录向连接池获取数据库连接而不得、等待的线程数。

1.3、PoolEntry

连接池中存储的对象不是Connection,也不是Connection的代理对象,而是PoolEntry。

PoolEntry持有数据库连接对象Connection,Connection在实例化PoolEntry前创建。PoolEntry创建的过程中会同时创建两个ScheduledFuture任务:endOfLife和keepalive。

endOfLife提交MaxLifetimeTask定时任务,在PoolEntry创建后的maxLifetime(参数指定)执行。MaxLifetimeTask会关闭当前连接、同时根据需要创建新的连接加入到连接池。

keepalive提交KeepaliveTask周期性任务,在PoolEntry创建后按照keepalive(参数设定)周期性执行,在当前连接空闲的情况下检查连接是否可用(使用参数指定的ConnectionTestQuery进行测试),如果连接不可用则关闭并重新创建连接。

1.4、addConnectionExecutor

添加数据库连接的任务管理器。负责数据库连接的创建以及加入到连接池中(sharedList和handoffQueue)。

注意addConnectionExecutor本身是一个线程池ThreadPoolExecutor,线程池容量为最大数据库连接数,HikariPool初始化的过程中会以多线程(corePoolSize=CPU内核数量)的方式快速完成连接创建和池化,之后正常情况下会以单线程(corePoolSize=1)的方式创建数据库连接。

houseKeeperTask

是一个周期性线程池ScheduledExecutorService,初始化的时候创建,定时(housekeepingPeriodMs)执行,清理掉(关闭)连接池中多余的(大于最小空闲数)空闲连接,如果连接池中的连接数没有达到参数设置的数量(最大连接数、或者空闲连接没有达到最小空闲连接数)则创建连接。

1.5、HikariProxyConnection

HikariProxyConnection是数据库连接Connection的扩展类,应用层通过getConnection方法获取到的数据库连接其实不是不是数据库连接Connection、而是这个扩展类HikariProxyConnection。

使用扩展类HikariProxyConnection、而不是原生的数据库连接Connection的一个最直观的理由是,对于数据库连接池来说,连接的关闭方法close不是要关闭连接、而是要把连接交还给连接池。使用扩展类(或者代理类)就可以很容易的重写其close方法实现目标,而如果直接使用原生Connection的话,就没办法控制了。

closeConnectionExecutor

数据库连接池需要关闭的时候,通过closeConnectionExecutor线程池提交,关闭连接后closeConnectionExecutor还负责调用fillPool(),根据需要填充连接池。

2、看下一般的配置

此错误提示看上去像是在说 maxLifetime 设置过长了,在HikariPool内部有一个规则,就是maxLifetime的设置时间要小于数据库的timeout,所以排查下maxLifetime和timeout的值

2.1、目前我的配置

  • yml配置
 datasource:
   hikari:
      max-lifetime: 1800000  # 设置为 30 分钟(比 MySQL 默认空闲超时时间短)
      connection-test-query: SELECT 1
      leak-detection-threshold: 2100 # 2秒检测连接泄露
      connection-timeout: 30000  # 30秒的连接超时
      idle-timeout: 600000  # 10分钟的空闲超时
  • mysql配置
    mysql有关连接超时的参数有两个,一个是interactive_timeout 另一个是wait_timeout ,单位都是s
show variables like '%timeout%';
Variable_namevalue
interactive_timeout7200
wait_timeout7200

可以看出 max-lifetime 远小于 数据库的timeout时间,并且idle-timeout也小于max-lifetime配置

最终经过多方的源码翻看,原来需要再配置一个参数

datasource:
  hikari:
    keepalive-time: 509999

keepalive-time小于idle-timeout就行

3、总结

核心参数配置
数据库timeout > hikari.max-lifetime > hikari.idle-timeout > hikari.keepalive-time
完美解决问题

补充:4、Hikari连接池高性能的原因

  • 1、采用自定义的FastList替代了ArrayList,FastList的get方法去除了范围检查rangeCheck逻辑,并且remove方法是从尾部开始扫描的,而并不是从头部开始扫描的。因为Connection的打开和关闭顺序通常是相反的

  • 2、初始化时创建了两个HikariPool对象,一个采用final类型定义,避免在获取连接时才初始化,因为获取连接时才初始化就需要做同步处理

  • 3、Hikari创建连接是通过javassist动态字节码生成技术创建的,性能更好

  • 4、从连接池中获取连接时对于同一个线程在threadLocal中添加了缓存,同一线程获取连接时没有并发操作

  • 5、Hikari最大的特点是在高并发的情况下尽量的减少锁竞争


http://www.kler.cn/news/355493.html

相关文章:

  • 健康推荐新动力:SpringBoot智能系统
  • PostgreSQL与MySQL在语法上的区别
  • 【飞腾加固服务器】全国产化解决方案:飞腾FT2000+/64核,赋能关键任务保驾护航
  • 链动2+1芸众商城421+全插件独立版源码
  • C++ 在项目中使用GDB
  • element设置时间和日期框早于现在的时间和日期禁用
  • 微积分复习笔记 Calculus Volume 1 - 3.1 Defining the Derivative
  • 随机抽取学号
  • Spring Boot 3新特性@RSocketExchange轻松实现消息实时推送
  • LlamaIndex核心概念查询管道(Query Pipelines)简介
  • java-poi实现自定义注解excel生成合并单元格数据导出
  • VScode运行C语言终端输出中文乱码问题解决方案
  • qt+opengl 实现一个3D立体体
  • [LeetCode 题3] 没有重复字符的最长的子字符串
  • Excel制作工资表
  • Server-Sent Event(SSE) GPT场景实现
  • Python脚本实现发送QQ邮件
  • RabbitMQ 入门(八)SpringAMQP消息转换器
  • jmeter中对于有中文内容的csv文件怎么保存
  • C语言复习第4章 数组