连接池监控与健康检查

连接池调优做了一大堆,性能数据也好看。但有一天线上突然大量超时,你慌不慌?

我告诉你,不慌是假的。调优的成果能不能持续,就看监控和健康检查做没做到位。说白了,连接池是个黑盒子,你不盯着它,它就敢给你搞事情。

这一章,咱们把盖子掀开,看看怎么监控、怎么检查、怎么在连接挂掉之前把它踢出去。

4.1 监控指标

监控不是什么都看,要有重点。我个人的习惯是,先盯死四个核心指标。

指标 含义 计算公式/来源 建议阈值
活跃连接数 当前正在被使用的连接数量 pool.getActive() 最大值 < 池大小 × 80%
空闲连接数 当前池中未被占用的连接数量 pool.getIdle() 最小值 > 核心连接数 × 20%
等待获取连接线程数 因池中无可用连接而阻塞的线程数 pool.getPending() 接近 0
连接获取超时次数 调用getConnection()超时的累计次数 pool.getConnectionTimeoutCount() 0

在项目中,我通常会关注前两个指标的变化趋势。如果活跃连接数持续接近池最大值,而等待线程数也开始上涨,就说明池大小不够,或者某个慢查询占着连接不释放。

监控黄金法则:不要只看平均值,要看 P99 和 P999。连接获取时间的 P99 突然从 5ms 飙到 50ms,很可能就是连接泄漏的前兆。

4.2 健康检查策略

你想想看,数据库连接可能因为网络闪断、防火墙超时、数据库重启而变成「死连接」。池里面全是死连接,新的请求进来根本拿不到可用连接。

健康检查就是解决这个问题的。

4.2.1 验证查询

最常见的做法,是在连接归还或借出时,执行一个轻量级的 SQL 查询,确认连接还活着。

# HikariCP 配置示例
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.validation-timeout=5000

这里要注意,查询越简单越好。SELECT 1 就够了,别搞 SELECT * FROM big_table。我曾经见过有人用 SELECT COUNT(*) FROM users 做验证,结果线上压力大时,验证查询本身就把数据库拖慢了。

4.2.2 空闲连接扫描

连接一直闲置也会被数据库侧断开。HikariCP 默认会每隔 30 秒扫描一次空闲连接,把那些超过 maxLifetime 的连接干掉。

我个人推荐开启 leakDetectionThreshold,这能帮你快速定位忘记归还连接的代码。

spring.datasource.hikari.leak-detection-threshold=60000

如果某个连接被占用了超过 60 秒还没归还,HikariCP 就会打印一条详细的堆栈日志。你在日志里搜一下"Connection leak",就知道哪个位置忘记 close 了。

避坑提醒:我曾经遇到一个项目,把 leakDetectionThreshold 设得太低,比如 5 秒。结果正常的事务(比如导出报表)每次都触发泄漏告警,告警系统直接被冲垮。建议设在实际业务最长事务时间的 1.5 到 2 倍。

4.3 健康检查的实现方式

不同连接池的检查机制略有差异,但核心思路一样。我们以 HikariCP 和 Druid 为例。

特性 HikariCP Druid
验证时机 借出时 / 空闲扫描时 借出时 / 归还时 / 空闲扫描时
验证查询 connectionTestQuery validationQuery
空闲扫描间隔 idleTimeout (默认 30 秒) timeBetweenEvictionRunsMillis
泄漏检测 leakDetectionThreshold removeAbandoned + removeAbandonedTimeout
连接回收方式 关闭并移除,新建替代 可选:关闭并移除 / 重建

对于 Druid,我还推荐开启 testWhileIdle 和 testOnReturn。不过要注意,testOnBorrow 会在每次获取连接时都执行验证,对高频调用的场景会造成额外开销。

实践建议:如果业务允许,优先使用空闲扫描 + 懒惰验证。即连接借出时不验证,只有在空闲时间超过一定阈值时才做验证。这样可以大幅降低验证查询对数据库的压力。HikariCP 默认就是这种策略。

4.4 监控集成方案

有了指标和检查,还得把数据收集起来,搭一个看得见的仪表盘。

4.4.1 通过 JMX 暴露指标

HikariCP 自带 JMX 支持,开启后就能通过 JConsole 或 VisualVM 看到池的运行时状态。

4.4.2 通过 Micrometer 上报到 Prometheus

// Spring Boot 中自动绑定 HikariCP 指标到 Micrometer
// 配合 micrometer-registry-prometheus 暴露 /actuator/prometheus 端点

// 关键指标名称:
// hikaricp_connections_active
// hikaricp_connections_idle
// hikaricp_connections_pending
// hikaricp_connections_timeout_total

嗯,这里要注意。Micrometer 默认会把所有池的指标都绑定上去。如果应用里配了多个数据源,指标名会自动带上池名称,丢到 Grafana 里做面板时记得按 pool 字段分组。

4.4.3 自定义健康检查端点

Spring Boot Actuator 的 /actuator/health 已经包含了数据源的健康状态。但默认只检查能不能获取一个连接,不够细。你可以扩展 HealthIndicator 来增加自定义逻辑。

@Component
public class ConnectionPoolHealthIndicator implements HealthIndicator {
    @Autowired
    private DataSource dataSource;

    @Override
    public Health health() {
        HikariDataSource hds = (HikariDataSource) dataSource;
        int active = hds.getHikariPoolMXBean().getActiveConnections();
        int idle = hds.getHikariPoolMXBean().getIdleConnections();
        int pending = hds.getHikariPoolMXBean().getThreadsAwaitingConnection();

        if (pending > 10) {
            return Health.down()
                .withDetail("active", active)
                .withDetail("idle", idle)
                .withDetail("pending", pending)
                .build();
        }
        return Health.up()
            .withDetail("active", active)
            .withDetail("idle", idle)
            .build();
    }
}

4.5 告警与自动化处理

光有面板还不够,得有告警。我习惯把告警做三层:

  • 信息告警:连接池使用率超过 70%,发到钉钉/飞书,让值班人员关注。
  • 警告告警:连接获取超时次数 > 5 次/分钟,发短信,要求 10 分钟内响应。
  • 严重告警:健康检查连续失败 3 次,直接调用脚本重启连接池或切到备用数据库。

自动化处理部分,我建议不要用连接池的自动恢复功能在线上直接生效。可以在健康检查失败时,先走容灾切换,再告警让运维介入排查原因。自动恢复容易掩盖根本问题,导致故障反复出现。

核心原则:监控和健康检查不是运维的附属品,而是连接池调优的护栏。没有监控,调优就是盲人摸象。

连接池健康检查核心流程 连接获取请求 空闲连接池 活跃连接池 等待队列 健康检查执行 连接有效 连接无效 告警通知 已通过健康检查的连接可返回