从业务卡顿到数据库连接池耗尽:Spring Boot项目HikariCP超时问题实战排查

最近在项目中遇到了一次典型的连接池连接超时问题,这里把整个排查和解决过程分享出来,希望对大家有所帮助。

事情的起因:业务反馈卡顿,运维告警数据库负载飙升

一天早上,客服团队向我们反馈系统某些功能出现了明显的卡顿,尤其是写数据库相关的业务请求,响应时间变长甚至失败。与此同时,运维那边监控报警说,数据库的CPU和IO负载突然飙升,连接数也接近了上限。

奇怪的是,数据库负载过了一阵子又自动恢复正常,好像什么异常都没发生过。我们一开始以为可能是业务高峰或者偶发的长事务占用了连接资源,导致短时压力过大。

初步排查:数据库超时,但没有找到明显的死锁或长事务

于是,我们查看了系统日志,发现有很多数据库连接池超时的错误,大致是:

csharp 复制代码
HikariPool-1 - Connection is not available, request timed out after 30003ms.

这意味着系统在请求数据库连接时,连接池里的连接已经用完,且等待超过30秒也没拿到可用连接。

但奇怪的是,数据库端没有明显的死锁或慢查询,也没发现长时间未提交的事务。考虑到当时业务量可能突然激增,我们认为连接池短时被抢光了。

当时并没有对配置做大幅调整,只是临时增加了连接池最大连接数一点点。

事态再起:第二天又出现相同问题,且更频繁

没想到第二天,这个问题又复现了,且变得更加频繁。我们注意到,系统中A服务调用B服务的写数据库操作时,B服务的方法是用事务注解的。

进一步调查发现,A服务本身使用了一个线程池,线程池的线程数量明显多于数据库连接池的最大连接数。

换句话说,线程池里很多线程同时调用事务方法写数据库,导致连接池的连接被快速抢占完,后续请求线程只能阻塞等待,最终超时失败。

找到根源:线程数大于连接池连接数,长事务占用连接导致连接池耗尽

总结下来,这次连接池耗尽的主要原因是:

  • 线程池线程数多于连接池最大连接数,大量线程并发抢连接
  • 事务方法执行期间持有连接时间较长,连接释放慢
  • 数据库短时负载升高,响应变慢,连接回收变慢,导致连接池"拥堵"

这些因素叠加,造成了连接池连接无法及时释放和重用,最终触发等待超时异常。

解决方案:调大连接池,优化线程池和业务逻辑,合理削峰

针对这个问题,我们做了如下调整:

  1. 调大连接池最大连接数,从10提升到30,匹配并发线程池的线程数。
  2. 优化线程池配置,控制线程数量,避免无限增长,减少连接压力。
  3. 拆分业务流程,将长耗时的远程调用从事务方法中剥离,减少持有连接的时间。
  4. 引入异步队列,对写数据库请求做削峰处理,避免瞬时高并发压垮连接池。
  5. 增加监控,实时观察连接池活跃连接数和线程等待数,及时预警调整。

实践效果:系统稳定,数据库负载均衡,连接超时消失

做完调整后,系统的数据库写入性能明显改善,连接池连接等待时间大幅减少,数据库负载趋于平稳。

业务请求响应速度恢复正常,客服也没有再反馈卡顿。


总结

  • 在高并发场景下,线程池线程数和数据库连接池大小必须合理匹配,否则极易出现连接资源耗尽。
  • 长事务持有数据库连接时间越短越好,否则会阻塞连接池资源。
  • 监控和预警体系很关键,能提前发现连接池压力异常,防患于未然。
  • 业务拆分和异步处理是应对高并发写数据库压力的有效手段。

希望这篇分享能帮你在类似场景中少走弯路,如果你也遇到相关问题,欢迎留言交流!


相关推荐
胚芽鞘68144 分钟前
关于java项目中maven的理解
java·数据库·maven
岁忧2 小时前
(LeetCode 面试经典 150 题 ) 11. 盛最多水的容器 (贪心+双指针)
java·c++·算法·leetcode·面试·go
CJi0NG2 小时前
【自用】JavaSE--算法、正则表达式、异常
java
Nejosi_念旧2 小时前
解读 Go 中的 constraints包
后端·golang·go
风无雨2 小时前
GO 启动 简单服务
开发语言·后端·golang
Hellyc2 小时前
用户查询优惠券之缓存击穿
java·redis·缓存
小明的小名叫小明2 小时前
Go从入门到精通(19)-协程(goroutine)与通道(channel)
后端·golang
斯普信专业组2 小时前
Go语言包管理完全指南:从基础到最佳实践
开发语言·后端·golang
今天又在摸鱼3 小时前
Maven
java·maven
老马啸西风3 小时前
maven 发布到中央仓库常用脚本-02
java·maven