Spring Boot集成Hikari CP数据库故障后连接恢复异常问题咨询
问题背景
我们有多款基于Spring Boot 2.2.4、Hikari CP 3.4.2对接PostgreSQL的微服务,近期遇到持续约30秒的数据库故障,连接中断后,18台镜像、配置完全一致的容器中,有2台无法恢复数据库连接,其余容器均正常恢复。
故障发生时所有容器均上报broken pipe、连接丢失错误,数据库恢复后仅异常容器抛出如下异常栈:
org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:402) ... 20 more Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms. at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:689) at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:196) at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:161) at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128) at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38) at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:104) ... 30 more Caused by:org.postgresql.util.PSQLException: This connection has been closed. at org.postgresql.jdbc.PgConnection.checkClosed(PgConnection.java:857) at org.postgresql.jdbc.PgConnection.setNetworkTimeout(PgConnection.java:1639) at com.zaxxer.hikari.pool.PoolBase.setNetworkTimeout(PoolBase.java:556) at com.zaxxer.hikari.pool.PoolBase.isConnectionAlive(PoolBase.java:169) at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:185) ... 35 more
此前同环境下的数据库故障切换测试中,Hikari均能正常自动恢复连接,本次故障中1台异常容器1小时后自行恢复,另一台需重启后才恢复。按照Hikari的设计机制,失效连接会被标记后从连接池驱逐,为何同配置下仅部分容器出现连接无法恢复的问题?
现有Hikari配置
- allowPoolSuspension: false
- connectionInitSql: none
- connectionTestQuery: none
- connectionTimeout: 30000ms
- idleTimeout: 600000ms
- initializationFailTimeout: 1
- isolateInternalQueries: false
- leakDetectionThreshold: 0
- maxLifetime: 1800000ms
- maximumPoolSize: 15
- minimumIdle: 15
- validationTimeout: 5000ms
根因分析
1. Hikari CP 3.4.2版本的已知边缘场景缺陷
你使用的HikariCP 3.4.2版本存在连接驱逐逻辑的偶发竞态条件:当数据库断连时间刚好落在连接池的HouseKeeper调度周期间隙,且异常节点的所有连接在断连瞬间都处于被借出/正在被校验的状态时,Hikari的isConnectionAlive校验触发setNetworkTimeout失败后,存在极小概率不会将该连接标记为失效驱逐,反而会把连接放回池中,导致后续所有拿连接的请求都会在校验步骤超时,最终占满整个连接池的所有槽位。
2. 同配置仅部分节点异常的原因
18台节点的连接使用负载完全一致是理论状态,实际运行中不同节点的请求密度、连接借出时序存在毫秒级差异:故障发生时仅那2台节点的所有15个连接刚好都处于「借出后返回池的校验阶段」触发了上述竞态缺陷,其余节点都有至少1个连接能被正常驱逐、后续新建有效连接完成恢复。
3. 恢复时间差异的原因
其中1台节点1小时后恢复是因为刚好有连接达到了maxLifetime阈值被清理,坏连接全部淘汰后新建了正常连接。另一台节点因为流量特征问题,没有触发连接淘汰逻辑,坏连接一直占满池,只能重启恢复。
修复建议
- 优先升级HikariCP到4.0.3及以上版本,该版本已修复上述连接校验的竞态缺陷
- 如暂时无法升级,可添加
connectionTestQuery: SELECT 1配置,绕过setNetworkTimeout的校验逻辑,降低缺陷触发概率 - 可将maxLifetime调整为300000ms(5分钟),缩短坏连接的最长留存时间
内容的提问来源于stack exchange,提问作者kozgurd

