数据库连接池耗尽故障排查实战 一、故障现象 数据库连接池耗尽是生产环境中常见且严重的故障,典型现象包括:
应用侧 :接口超时、请求堆积、大量 ConnectionPoolTimeoutException
数据库侧 :连接数接近上限、新连接建立失败、慢查询增多
监控告警 :连接池使用率 > 90%、活跃连接数异常、等待队列堆积
1 2 3 4 5 6 典型错误日志: Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms. Caused by: com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator$1: Connection acquisition timeout after 30000 ms
二、故障影响
影响维度
具体表现
业务影响
接口超时、用户请求失败、交易中断
系统影响
线程池阻塞、内存增长、CPU 升高
数据库影响
连接数爆满、锁等待、性能下降
连锁反应
服务雪崩、级联故障、数据不一致
三、快速诊断流程 3.1 诊断流程图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ┌─────────────────┐ │ 发现连接池告警 │ └────────┬────────┘ ▼ ┌─────────────────┐ │ 检查连接池状态 │ └────────┬────────┘ ▼ ┌──────────────┴──────────────┐ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 连接数是否超限?│ │ 是否有连接泄漏?│ └────────┬────────┘ └────────┬────────┘ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 检查数据库连接数 │ │ 检查长事务/未关闭│ └────────┬────────┘ └────────┬────────┘ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 慢查询/锁等待? │ │ 代码修复 + 重启 │ └────────┬────────┘ └─────────────────┘ ▼ ┌─────────────────┐ │ 扩容/优化/限流 │ └─────────────────┘
3.2 应用侧诊断命令 1 2 3 4 5 6 7 8 9 10 11 12 13 curl -s http://localhost:8080/actuator/metrics/hikaricp.connections.active curl -s http://localhost:8080/actuator/metrics/hikaricp.connections.pending curl -s http://localhost:8080/actuator/metrics/hikaricp.connections.max jstack <PID> | grep -A 30 "pool-" | head -100 jstack <PID> | grep -B 5 "HikariPool" | grep -A 5 "WAITING" java -jar /opt/app/app.jar --spring.config.location=file:/opt/app/application.yml
3.3 数据库侧诊断命令 MySQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 SHOW STATUS LIKE 'Threads_connected' ;SHOW VARIABLES LIKE 'max_connections' ;SELECT host, db, user , command, state, time , info FROM information_schema.processlist ORDER BY time DESC LIMIT 20 ; SELECT user , host, COUNT (* ) as conn_count FROM information_schema.processlist GROUP BY user , host ORDER BY conn_count DESC ;SELECT * FROM information_schema.innodb_lock_waits;SELECT * FROM performance_schema.data_lock_waits;SELECT * FROM information_schema.innodb_trx WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60 ;
PostgreSQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 SELECT count (* ) FROM pg_stat_activity;SHOW max_connections;SELECT pid, usename, datname, client_addr, application_name, state, wait_event_type, wait_event, query, query_start FROM pg_stat_activity ORDER BY query_start LIMIT 20 ; SELECT * FROM pg_locks WHERE NOT granted;SELECT pid, now() - pg_stat_activity.query_start AS duration, query FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) > interval '1 minute' ORDER BY duration DESC ;
四、常见原因分析 4.1 连接泄漏(最常见) 现象 :连接数持续增长,不释放
原因 :
代码中未正确关闭 Connection/Statement/ResultSet
异常处理不当,finally 块未执行关闭
使用 try-with-resources 不规范
排查方法 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void leakExample () throws SQLException { Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM users" ); } public void properExample () throws SQLException { try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM users" )) { while (rs.next()) { } } }
HikariCP 泄漏检测 :
1 2 3 4 5 6 7 spring: datasource: hikari: leak-detection-threshold: 60000 connection-timeout: 30000 max-lifetime: 1800000
4.2 慢查询导致连接占用 现象 :连接数正常,但活跃连接占比高,大量连接处于执行状态
排查方法 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 SHOW VARIABLES LIKE 'slow_query_log' ;SHOW VARIABLES LIKE 'long_query_time' ;SELECT id, user , host, db, command, time , state, info FROM information_schema.processlist WHERE command != 'Sleep' AND time > 5 ORDER BY time DESC ;SELECT pid, query, now() - query_start AS duration FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 seconds' ;
解决方案 :
优化慢查询(添加索引、改写 SQL)
设置查询超时:statement_timeout
增加连接池大小(临时方案)
4.3 事务过长 现象 :连接持有时间长,事务未提交
排查方法 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query, trx_operation_state, trx_tables_in_use, trx_tables_locked, trx_lock_structs, trx_lock_rows, trx_isolation_level FROM information_schema.innodb_trxWHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 30 ;SELECT pid, usename, datname, client_addr, now() - xact_start AS transaction_duration, state, query FROM pg_stat_activityWHERE xact_start IS NOT NULL AND now() - xact_start > interval '1 minute' ORDER BY transaction_duration DESC ;
解决方案 :
拆分大事务
避免在事务中调用外部 API
设置事务超时
4.4 连接池配置不当 现象 :连接池大小设置过小,无法应对流量高峰
排查方法 :
1 2 3 4 5 6 7 8 9 10 spring: datasource: hikari: maximum-pool-size: 10 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 connection-test-query: SELECT 1
推荐配置 (参考 HikariCP 官方建议):
1 2 3 4 5 6 7 8 9 10 11 12 spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 10 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 connection-timeout: 30000 validation-timeout: 5000 leak-detection-threshold: 60000
4.5 数据库连接数上限 现象 :应用连接池正常,但数据库拒绝新连接
排查方法 :
1 2 3 4 5 6 7 8 SHOW VARIABLES LIKE 'max_connections' ;SHOW STATUS LIKE 'Max_used_connections' ;SELECT @@max_connections , @@max_user_connections ;SHOW max_connections;SELECT * FROM pg_stat_activity WHERE datname = current_database();
解决方案 :
调整数据库最大连接数
使用连接池中间件(PgBouncer、ProxySQL)
应用层限流
五、应急响应方案 5.1 紧急恢复步骤 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 mysql -e "SET GLOBAL max_connections = 1000;" mysql -e "KILL <thread_id>;" psql -c "SELECT pg_terminate_backend(<pid>);" systemctl restart application
5.2 连接池临时调整 1 2 3 4 5 spring: datasource: hikari: maximum-pool-size: 50
1 2 3 4 HikariConfig config = new HikariConfig ();config.setMaximumPoolSize(50 ); HikariDataSource ds = new HikariDataSource (config);
5.3 数据库侧应急 1 2 3 4 5 6 7 8 9 10 11 12 SET GLOBAL max_connections = 1000 ;SELECT CONCAT('KILL ' , id, ';' ) FROM information_schema.processlist WHERE command = 'Sleep' AND time > 300 ;ALTER DATABASE mydb WITH CONNECTION LIMIT 50 ;
六、监控告警配置 6.1 关键监控指标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 连接池使用率 hikaricp_connections_active / hikaricp_connections_max # 连接等待队列 hikaricp_connections_pending # 连接获取延迟 hikaricp_connections_acquire_seconds # 数据库连接数 mysql_global_status_threads_connected / mysql_global_variables_max_connections # 慢查询数量 mysql_global_status_slow_queries # 长事务数量 mysql_info_schema_innodb_trx_trx_state_count{trx_state="RUNNING"}
6.2 告警规则 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 groups: - name: database-connection rules: - alert: ConnectionPoolHighUsage expr: hikaricp_connections_active / hikaricp_connections_max > 0.8 for: 5m labels: severity: warning annotations: summary: "连接池使用率过高" description: "{{ $labels.instance }} 连接池使用率 {{ $value | humanizePercentage }} " - alert: ConnectionPoolExhausted expr: hikaricp_connections_pending > 0 for: 2m labels: severity: critical annotations: summary: "连接池耗尽" description: "{{ $labels.instance }} 有 {{ $value }} 个连接在等待" - alert: DatabaseConnectionHigh expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.9 for: 5m labels: severity: warning annotations: summary: "数据库连接数过高" description: "{{ $labels.instance }} 连接数使用率 {{ $value | humanizePercentage }} " - alert: LongRunningTransaction expr: mysql_info_schema_innodb_trx_trx_started_timestamp < time() - 300 for: 5m labels: severity: warning annotations: summary: "发现长事务" description: "{{ $labels.instance }} 有事务运行超过 5 分钟"
6.3 Grafana 仪表盘 建议监控面板包含:
连接池使用率趋势图
活跃/空闲/等待连接数
连接获取延迟 P95/P99
数据库连接数 vs 上限
慢查询数量趋势
长事务列表
七、预防措施 7.1 代码层面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { } @Configuration public class DataSourceConfig { @Bean public DataSource dataSource () { HikariConfig config = new HikariConfig (); config.setConnectionTimeout(30000 ); config.setMaxLifetime(1800000 ); config.setLeakDetectionThreshold(60000 ); return new HikariDataSource (config); } } @Transactional public void badExample () { repository.save(entity); restTemplate.getForObject("http://external/api" , String.class); } @Bean public DataSourceHealthIndicator dataSourceHealthIndicator (DataSource dataSource) { return new DataSourceHealthIndicator (dataSource); }
7.2 配置层面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 10 connection-timeout: 30000 validation-timeout: 5000 idle-timeout: 600000 max-lifetime: 1800000 leak-detection-threshold: 60000 connection-test-query: SELECT 1 register-mbeans: true jpa: properties: hibernate: jdbc: statement_fetch_size: 100 batch_versioned_data: true
7.3 架构层面
读写分离 :将读请求分发到从库
分库分表 :降低单库连接压力
连接池中间件 :使用 PgBouncer/ProxySQL
服务降级 :配置熔断器(Hystrix/Resilience4j)
限流保护 :限制并发数据库请求数
八、故障复盘模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 数据库连接池耗尽故障复盘 ## 故障信息 - 故障时间:YYYY-MM-DD HH:MM- 恢复时间:YYYY-MM-DD HH:MM- 持续时长:XX 分钟- 影响范围:XX 服务、XX 用户## 故障现象 (描述监控告警、用户反馈、错误日志等) ## 根本原因 (5 Why 分析法找出根本原因) ## 处理过程 1. HH:MM 发现告警2. HH:MM 开始排查3. HH:MM 定位原因4. HH:MM 实施修复5. HH:MM 验证恢复## 改进措施 - [ ] 短期:立即修复的问题- [ ] 中期:需要开发排期的优化- [ ] 长期:架构层面的改进## 经验教训 (值得总结的经验)
九、总结 数据库连接池耗尽故障的排查要点:
快速定位 :区分是应用侧还是数据库侧问题
找准原因 :连接泄漏、慢查询、长事务、配置不当
应急恢复 :杀死长事务、临时扩容、重启应用
监控预防 :建立完善的监控告警体系
代码规范 :正确使用连接池,避免资源泄漏
记住:连接池不是越大越好 ,合理配置 + 规范代码 + 完善监控才是王道。