← 中文资源

简体中文版本

HikariCP — Connection is not available,请求 30 秒后超时

诊断 HikariCP 连接池耗尽,读取活跃与等待计数,找到持连接不放的代码(未关闭、长事务),并正确设定池大小。

阅读英文原文

症状

  异常文本自带配置预算与池名:Connection is not available, request timed out after 30000ms。指标对子会告诉你属于哪种失败模式:active 钉在最大值且 pending 每秒增长是经典饱和;active 未满却超时则指向连接级错误或泄漏检测期。负载停止立即恢复、持续流量下持续恶化,说明是“持有时长 × 到达速率”压垮了固定池。决定性工具是泄漏检测阈值——它打印每个超阈值持有连接的获取栈,把谜团变成文件与行号。

诊断思路

  先读 active/pending 指标确认饱和形态;再用 jstack 看谁在 HikariPool.getConnection 上等待、谁拿着连接在做什么;然后开启 leak-detection-threshold 拿到持有着的代码路径;最后审计 @Transactional 边界——事务方法里做远程调用,会把池连接按整个调用时长扣住,每个慢下游都在收缩池子。语句与结果集泄漏同样扣住底层连接。

根因

  连接池发放固定数量的连接。耗尽意味着持有者保留连接的时间超过了请求到达速率:泄漏会永久移除一部分,长事务成倍放大每请求持有时长。新请求排队到 connectionTimeout 后以 SQLTransientConnectionException 失败。

修复与验证

  try-with-resources 确定性关闭(连接、语句、结果集三件套一个都不能少);缩小事务范围——池是“连接时间预算”不是线程预算,HTTP 调用放事务外、毫秒级 DAO 操作才进事务;池大小按实测持有时间推导而不是核数;预发开泄漏检测;让每个下游调用的超时都小于 connectionTimeout,慢依赖只拖垮自己的请求而不是整个池。验证时峰值负载下 active 低于最大值、pending 趋零、完整浸泡测试日志里零获取超时。池超大只会把队列挪到数据库全局连接上限,殃及其他服务。

代码示例

java.sql.SQLTransientConnectionException:
HikariPool-1 - Connection is not available, request timed out
after 30000ms.
hikaricp.connections.active    = 10   <- pinned at max
hikaricp.connections.pending    = 214  <- growing every second
try (Connection c = ds.getConnection();
     PreparedStatement ps = c.prepareStatement(sql);
     ResultSet rs = ps.executeQuery()) {
    return map(rs);
} // all three closed on every path
public Order place(OrderRequest req) {
    var quote = pricingClient.quote(req);    // HTTP call: OUTSIDE transaction
    return tx.run(() -> orderDao.insert(quote.toOrder())); // milliseconds
}
spring:
  datasource:
    hikari:
      leak-detection-threshold: 60000
      maximum-pool-size: 12        # from measured hold-time, not cores