开放问答
HikariCP activeCount 只有 3/20 却偶发 ConnectionTimeoutException:连接被事务「隐形占用」怎么排查
该问题的正文、回答与评论。
提问者 程知远 · 状态 open
HikariCP连接池性能调优Java事务
【背景】
Spring Boot 3.2 + JDK 17 + MySQL 8.0 + HikariCP 5.1,池大小 20,connectionTimeout=3000ms,minimumIdle=5,maxLifetime=1800s。核心查询接口 QPS 约 300,P50 稳定 25ms,但每天偶发(约 20–40 次)抛 SQLTransientConnectionException: Connection is not available, request timed out after 3000ms。关键矛盾:报错时刻 Grafana 面板上 active 最多 3–5、idle 还有 15+,远未打满,但就是 acquire 超时。
【现象】
- 报错窗口集中在每天 14:00–14:30(定时任务批量更新)和 21:00–21:10(对账批处理),其余时间基本不出现;
- 把
leakDetectionThreshold调到 3000ms 后,日志里没有出现 "A connection was not returned within the leak threshold" 告警——说明连接不是"忘了归还"; - HikariCP
PoolStats定时打印(每 5s)在报错时刻附近显示:active=3, idle=17, waiting=1——有线程在等,但池里有空闲连接,不符合"池满"直觉; - 抓了一次
jstack(报错时刻 ±2s):看到 2 个线程 BLOCKED 在HikariPool.getConnection的synchronized块上,而持锁线程正在跑一次 HTTP 调用(okhttp3.RealCall.execute,下游 P99 约 2s); - 该 HTTP 调用发生在
@Transactional方法体内,夹在两次 DAO 调用之间——我怀疑连接被借出后,在事务里做非 DB 操作时,HikariCP 内部状态机把它既不算 active 也不算 idle。
【已尝试】
connectionTimeout从 3000 → 10000:报错次数没明显减少;minimumIdle从 5 → 10:没用;- 把出问题的
@Transactional方法里的 HTTP 调用抽到事务外(改为TransactionSynchronization.afterCompletion回调):复现频率从 ~30 次/天 降到 ~5 次/天,但没完全消除; - 在 HikariCP 源码
HikariPool.getConnection()里加了一行日志打印state字段:报错时state=RUNNING,pool内部pool队列长度 17,但poolEntry的state是ALREADY_BORROWED——有连接"借出去了但没在执行 SQL"。
【期望得到的答案形态】
- HikariCP 计数语义:
active / idle / waiting三个计数到底怎么算?一个连接被getConnection借出后、在@Transactional里做非 DB 操作(HTTP / 分布式锁等待),它算 active 还是 idle?leakDetectionThreshold的判定起点是getConnection返回时刻还是第一次executeQuery时刻? - 精准观测手段:如果确实是"连接被借出后在事务里做了长耗时非 DB 操作",有没有比
leakDetectionThreshold更精准的观测方式?比如HikariDataSource的beforeConnect / afterConnect回调、Connection的ConnectionEventListener、或者 HikariCP 自带的MetricsTrackerFactory里有没有"借出后未执行 SQL 时长"的指标? - 排查 SOP:一份"连接池偶发超时但 active 不高"的系统排查清单:jstack 看持锁线程 → HikariPool 内部
poolEntry.state分布 → 事务边界里有没有非 DB 调用 → 下游 P99 与 connectionTimeout 的关系,一步步怎么收敛到根因。
评论
暂无评论。