记一次Hikari数据库连接池连接失败问题

HiKari数据库连接池

HiKari源于日语“光”的意思,HiKariCP顾名思义就是 和光速一样快,HiKariCP是数据库连接池的一个后起之秀,号称性能最好,稳定性也不错,完美地PK掉其他连接池。
这里提供一篇文章介绍主流Java数据库连接池比较及前瞻,文中重点介绍了当前主流开源数据库连接池(比如C3P0、DBCP、Tomcat Jdbc Pool、Druid和Hikaricp)的性能分析和功能比较,有一定的参考价值.

回归到本文正题,在开发一个新项目时遇到一个问题——在定时任务中需要向数据库进行查询,插入操作,在项目运行一段时间后,Hikaricp连接池获取不到连接数而连接不到数据库,导致请求失败。
具体异常如下:

2020-07-27T18:00:11.092+08:00 ERROR sam [Weather_task-1] [o.s.s.support.TaskUtils$LoggingErrorHandler:95] Unexpected error occurred in scheduled task
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 10012ms.
	at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:305)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:573)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:361)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
	at com.XXX.sample.service.weather.WeatherService$$EnhancerBySpringCGLIB$$5c6f938c.insertWeather(<generated>)
	at com.XXX.sample.service.task.WeatherTaskService.doSyn(WeatherTaskService.java:69)
	at com.XXX.sample.service.task.WeatherTaskService.synWeather(WeatherTaskService.java:45)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
	at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 10012ms.
	at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:695)
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:197)
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162)
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128)
	at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:261)
	... 26 common frames omitted
Caused by: org.postgresql.util.PSQLException: This connection has been closed.
	at org.postgresql.jdbc.PgConnection.checkClosed(PgConnection.java:783)
	at org.postgresql.jdbc.PgConnection.setNetworkTimeout(PgConnection.java:1556)
	at com.zaxxer.hikari.pool.PoolBase.setNetworkTimeout(PoolBase.java:560)
	at com.zaxxer.hikari.pool.PoolBase.isConnectionAlive(PoolBase.java:173)
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:186)
	... 29 common frames omitted

问题分析:
1、系统的并行环境并没有此异常,生产环境的差异只是数据库部署在不同的主机上;

2、访问数据库命令的网络耗时正常,数据库端收到连接请求,且该时段数据库连接数并没有耗尽;

3、数据库管理员在异常时间段查询得知,不存在慢sql语句,不存在不合理设置索引导致此异常

既然环境没有什么问题,那么只能充HiKari的配置上入手。
以下是异常的数据源配置:

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="jdbcUrl" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
        <!-- 配置连接池最小值、最大值 -->
        <property name="minimumIdle" value="5" />
        <property name="maximumPoolSize" value="15" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="connectionTimeout" value="10000" />
        <property name="idleTimeout" value="600000" />
        <property name="maxLifetime" value="83460000" />
    </bean>

后四个重要参数的解释:

1).connectionTimeout:等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:10秒

2).idleTimeout:此属性控制允许连接在池中闲置的最长时间,此设置仅适用于minimumIdle设置为小于maximumPoolSize的情况 默认:600000(10分钟)

3).maxLifetime:一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:23.3小时,建议设置比数据库超时时长少30秒

4).maximumPoolSize:连接池中允许的最大连接数(包括空闲和正在使用的连接)。缺省值:10;

通过分析可以看到:
1.connectionTimeout采用默认值10s,应该不会有太大问题。
2.maximumPoolSize根据业务量的设置,不会有太大问题。
3.maxLifetime如果过长,且idleTimeout没有时间限制时,会导致连接数很大,空闲连接一直得不到释放,严重挤占资源,容易引起连接数不够的问题。
4.idleTimeout只有在minimumIdle设置为小于maximumPoolSize的情况下才生效,minimumIdle默认是等于maximumPoolSize,我已经设置了idleTimeout,应该也没问题。
5.未配置destroy-method=“close”,导致连接使用完以后,由于我配置的最大生存时间还未到期,没有释放该连接,导致连接池中的连接被耗尽,获取连接失败

优化后的配置:

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="jdbcUrl" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
        <!-- 配置连接池最小值、最大值 -->
        <property name="minimumIdle" value="5" />
        <property name="maximumPoolSize" value="15" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="connectionTimeout" value="60000" />
        <property name="connectionTestQuery" value="SELECT 1"/>
        <property name="validationTimeout" value="5000"/>
        <property name="poolName" value="CcosDataSource"/>
        <property name="idleTimeout" value="60000" />
        <property name="maxLifetime" value="600000" />
    </bean>

相比较之前的配置,此次优化之处:

1、增加数据连接关闭方法,当从dataSource获取的连接使用完成后,调用close方法,以避免数据源对象仍然可用,造成连接泄露

2、连接空闲时间idleTimeout生效,减少空闲连接占用,尽快释放数据库连接

3、连接生命周期maxLifetime值设为100分钟,尽快释放数据库无效连接

4、增加连接池的用户定义名称

5、增加验证连接有效性的超时时间

6、增加连接有效性测试语句
修改完配置以后,再次部署运行:
目前系统暂未发生数据库连接异常现象,继续观察。

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值