数据库连接池是应用与数据库之间的缓冲层,管理连接的创建、复用和回收。连接池配置不当导致两类典型故障:连接数耗尽引发请求超时,或连接泄漏导致池空。本文以Java生态中应用最广的HikariCP和国产Druid为例,覆盖核心参数调优、泄漏检测和监控方案。
HikariCP连接池核心参数与容量规划
HikariCP以高性能著称,参数精简但每个都影响关键行为。连接池大小不是越大越好,PostgreSQL官方建议公式:(core_count * 2) + effective_spindle_count,SSD环境下spindle_count按1计算。
// HikariCP配置(Spring Boot application.yml)
spring:
datasource:
hikari:
# 连接池大小
maximum-pool-size: 10
minimum-idle: 10
# 连接生命周期
max-lifetime: 1800000 # 30分钟,必须小于数据库wait_timeout
idle-timeout: 600000 # 10分钟
connection-timeout: 30000 # 获取连接超时30秒
# 心跳检测
keepalive-time: 120000 # 每2分钟发心跳
validation-timeout: 5000 # 校验超时5秒
# 泄漏检测
leak-detection-threshold: 60000 # 60秒未归还判定泄漏
# 连接池名称(日志中标识)
pool-name: OrderHikariPool
# 等价Java配置
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(10);
config.setMinimumIdle(10);
config.setMaxLifetime(1800000);
config.setIdleTimeout(600000);
config.setConnectionTimeout(30000);
config.setLeakDetectionThreshold(60000);
config.setPoolName("OrderHikariPool");
参数说明:
maximum-pool-size:连接池上限。4核CPU+SSD的典型配置为8-12,超过该值后吞吐量提升不明显但延迟增加。微服务多实例部署时需考虑实例数乘pool_size不超过数据库max_connections。
minimum-idle:HikariCP作者建议设为与maximum-pool-size相同,固定池大小避免动态创建连接的开销。
max-lifetime:连接最大存活时间。必须小于数据库的wait_timeout(MySQL默认8小时),否则连接被数据库单方面断开后应用仍在使用,报”Communications link failure”。设为30分钟是安全值。
leak-detection-threshold:连接借出后超过该时间未归还,记录堆栈到日志。生产环境设为60秒,开发环境设为0(关闭)或更短。
Druid连接池配置与SQL监控面板
Druid除了连接池功能,还内置SQL监控、慢查询日志和Wall防火墙。国内Java项目使用率高:
// Spring Boot application.yml
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/orders?useSSL=true&serverTimezone=Asia/Shanghai
username: root
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
# 连接池核心参数
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 30000 # 获取连接超时30秒
# 连接检测
validation-query: SELECT 1
test-while-idle: true # 空闲时检测
test-on-borrow: false # 借出时不检测(性能考虑)
test-on-return: false
time-between-eviction-runs-millis: 60000 # 检测间隔60秒
min-evictable-idle-time-millis: 300000 # 最小空闲5分钟
# 连接生命周期
phy-timeout-millis: 1800000 # 物理连接超时30分钟
phy-max-use-count: 10000 # 单连接最大使用次数
# 监控配置
filter:
stat:
enabled: true
slow-sql-millis: 1000 # 慢SQL阈值1秒
log-slow-sql: true
wall:
enabled: true # SQL防火墙
config:
select-all-column-allow: false # 禁止SELECT *
# 监控页面
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: ${DRUID_MONITOR_PASSWORD}
allow: 10.0.0.0/8 # 仅内网访问
web-stat-filter:
enabled: true
url-pattern: /*
session-stat-enable: true
test-while-idle为true时,连接在空闲检测周期(time-between-eviction-runs-millis)内被校验,失效连接自动回收。test-on-borrow为false避免每次借出都校验,降低延迟。这两个参数的组合是性能与可靠性的平衡点。
连接泄漏检测与排查实战
连接泄漏是连接未调用close()归还连接池,常见于异常路径未执行finally块。HikariCP的leak-detection-threshold开启后,日志输出示例:
WARN com.zaxxer.hikari.pool.ProxyLeakTask - Apparent connection leak detected.
Connection org.postgresql.jdbc.PgConnection@5f4a3e21 marked as leaked.
Stack trace:
at com.example.OrderRepository.findById(OrderRepository.java:45)
at com.example.OrderService.getOrder(OrderService.java:28)
at com.example.OrderController.getOrder(OrderController.java:22)
...
从堆栈定位到OrderRepository.java:45,检查是否遗漏close()。正确写法使用try-with-resources:
// 错误写法:异常时连接泄漏
public Order findById(Long id) throws SQLException {
Connection conn = dataSource.getConnection(); // 借出连接
PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE id = ?");
ps.setLong(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return mapOrder(rs); // 如果这里抛异常,conn永远不会close
}
rs.close();
ps.close();
conn.close(); // 异常路径不会执行
return null;
}
// 正确写法:try-with-resources自动关闭
public Order findById(Long id) throws SQLException {
String sql = "SELECT * FROM orders WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, id);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return mapOrder(rs);
}
}
} // conn, ps, rs 自动close,即使异常也执行
return null;
}
Spring生态中使用@Transactional注解管理事务时,Spring通过AOP在方法前后自动获取和归还连接,事务方法内手动getConnection()会从同一连接池借出新连接。嵌套调用时需确保事务传播行为正确。
连接池监控指标与容量预警
HikariCP通过Micrometer暴露指标,接入Prometheus后可设置告警规则:
# 核心监控指标
hikaricp_connections_active # 活跃连接数
hikaricp_connections_idle # 空闲连接数
hikaricp_connections_pending # 等待获取连接的线程数
hikaricp_connections_creation_seconds # 连接创建耗时
# Prometheus告警规则
- alert: HikariPoolConnectionExhaustion
expr: hikaricp_connections_pending > 0
for: 1m
labels:
severity: critical
annotations:
summary: "连接池 {{ $labels.pool }} 有线程等待获取连接"
- alert: HikariPoolConnectionLeak
expr: hikaricp_connections_active > hikaricp_connections * 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "连接池 {{ $labels.pool }} 活跃连接占比超80%持续5分钟"
连接池容量评估公式:最大并发请求数乘以单请求平均DB操作时间除以连接复用周期。例如1000 QPS、单查询5ms、连接复用周期100ms,需约50个连接。配合压测验证:逐步加压至目标QPS,观察connections_pending是否大于0、active是否接近maximum。如果持续打满,优先优化慢SQL而非盲目扩容连接池,过多连接反而增加数据库调度负担。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/shu-ju-ku-lian-jie-chi-diao-you-shi-zhan-hikaricp-yu-druid/