HikariCP生产环境报连接不可用但数据库无会话,如何排查根因
现象本质说明
你遇到的「Hikari连接池全部占满、但数据库侧查不到活跃会话」的矛盾,核心是两套完全不互通的统计逻辑:
- Hikari标记的
active活跃连接,指已经从池子里借出去、还没被应用代码还回来的连接——不管这个连接这会儿有没有给数据库发请求、有没有真的在跑SQL,只要没执行close()归还,Hikari就会把它算成活跃状态。 - 数据库通过
gv$session查出来的活跃会话,指此刻正在数据库进程里跑SQL的会话。那些被应用借走但根本没发请求的连接,数据库侧连痕迹都留不下,自然查不到活跃记录。
说白了,你看到的连接“活跃”,只是应用端把连接攥手里没还,不是连接真的在干活跑SQL。
常见根因定位方向
结合你提供的配置(除最大连接数设为20外其余全默认,泄漏检测功能完全关闭)和7*24高并发的生产运行场景,这类问题一般逃不出下面几类:
- 连接泄漏但没开检测,一直没被发现
你当前leakDetectionThreshold=0的配置,等于直接把Hikari自带的连接泄漏检测关了。常见的漏归还场景包括:- 代码里手动从数据源拿连接的时候,遇到异常分支没在finally块里写close()逻辑
- 异步线程、定时任务跑数据库操作时,事务管理器没把连接绑定到对应线程上下文,导致借出去的连接挂在异步线程上永远不释放
- 连接借出来之后,被业务逻辑拿着等非数据库操作(比如本地大计算、调第三方接口、等分布式锁),流程没跑完就不会还连接,全程也不会给数据库发任何SQL
- 业务线程阻塞,把连接一直占着不还
高并发下如果业务流程拿了连接之后卡在别的环节,连接就会被一直攥住:- 调第三方HTTP/RPC接口没设超时,对方接口一直不返回,连接就被一直占着
- 代码里的锁竞争、线程池任务排队,导致流程长时间挂起
- 本地处理大数据量、读写大文件这类慢操作,把连接持有时间拉得过长
- 僵死连接没被及时发现清理
共享数据库环境下,中间的防火墙、负载均衡经常会主动掐掉长时间没有数据传输的TCP连接。如果连接池没及时发现这类已经断了的“死连接”,就会出现应用侧还记着连接在池子里、但数据库侧早就把对应会话清掉的情况。你当前的配置下,因为没有设置显式的连接测试SQL,完全靠JDBC4驱动的isValid()做校验,而且只有当连接被借出去要用的时候才会做校验,平时不会主动查连接是不是还活着,很容易攒下一堆僵死连接。
可落地的排查动作
- 第一时间把Hikari的连接泄漏检测开起来:配置
spring.datasource.hikari.leak-detection-threshold=60000(也就是60秒阈值),只要连接被借出去超过60秒没还,Hikari会直接把对应持有连接的代码堆栈打出来,能快速定位到底是哪段代码漏了归还逻辑。 - 故障触发的时候立刻用jstack命令抓一份应用的线程堆栈,专门找持有JDBC连接的线程,看这些线程到底卡在哪:是卡在第三方接口调用、等锁还是漏了异常分支的close逻辑。
- 检查当前配置的不合理点:
- 你现在
minimumIdle=20和maximumPoolSize=20设成一样大,意味着连接池启动就直接拉满20个连接,连个空闲连接的缓冲都没有,一旦有几个连接被占了,很快就全池耗尽。可以结合实际并发量调整最小空闲连接数,留一点缓冲空间。 - 给Oracle驱动补上网络层的超时配置,别让连接无限等下去:在JDBC连接参数里加上
oracle.net.CONNECT_TIMEOUT=3000、oracle.jdbc.ReadTimeout=10000,避免网络层无响应把连接永久占死。
- 你现在
- 故障排查的时候别只查数据库的活跃会话,按你应用服务器的IP过滤,把数据库侧所有来自这个IP的会话(包括状态是inactive的空闲会话)都统计出来。如果算下来的连接数远小于Hikari日志里记的总连接数20,说明大部分TCP连接已经被中间网络设备掐断了,属于僵死连接没被及时清理的问题。
内容的提问来源于stack exchange,提问作者Rahel Aumann
相关产品推荐
相关产品推荐

