线上性能问题 - 连接池耗尽的排查思路
约 1260 字大约 4 分钟
2026-05-25
某个系统上线后出现连接池耗尽,如何一步步排查。可能还需要引用知识库中的具体配置示例,比如HikariCP的配置参数,或者Druid的监控方法。
遇到了线上连接池耗尽的问题,想要了解如何快速定位和解决。
问题背景
某核心业务系统上线后,用户反馈访问缓慢,部分接口出现超时甚至报错。通过监控平台查看,发现服务端异常日志中频繁出现如下错误:
Caused by: java.sql.SQLTransientConnectionException:
HikariPool-1 - Connection is not available, request timed out after 30000ms.这表明 数据库连接池资源已经耗尽,请求无法获取数据库连接,导致服务不可用。
排查思路与实战分析
确认问题表现
接口响应超时、数据库连接超时异常(如 SQLTransientConnectionException)。
监控指标:
- 数据库连接池的 Active Connections 接近或达到最大值。
- 连接池的 Wait Queue 长度持续增长。
- 数据库服务器的 CPU、内存或磁盘 I/O 负载过高。
初步定位原因
- 连接池配置不足:最大连接数(maximumPoolSize)设置过低,无法满足高并发需求。
- 连接未释放:代码中存在未正确关闭数据库连接(如未调用 connection.close())。
- 慢查询阻塞:SQL 查询执行时间过长,占用连接资源。
- 事务未提交:事务长时间未提交或回滚,导致连接被锁定。
- 连接泄漏:应用层代码逻辑错误导致连接未归还连接池。
详细排查步骤
检查连接池配置
HikariCP 配置示例:
spring:
datasource:
hikari:
#最小空闲连接,默认值10
minimum-idle: 5
# 连接池大小及超时等配置
maximum-pool-size: 20
# 空闲连接超时时间,
idle-timeout: 30000
# 连接超时时间
connection-timeout: 30000
pool-name: pool-1分析连接池使用情况
监控工具:
- Prometheus + Grafana:实时监控连接池的 Active Connections、Idle Connections 和 Wait Queue。
- Druid 内置监控:通过 /druid 管理页面查看连接池状态。
- HikariCP 统计:通过 JMX 查看 HikariPool 的 TotalConnections 和 ConnectionsAcquired。
关键指标:
- Active Connections:当前正在使用的连接数。
- Wait Queue:等待获取连接的请求数。
- Connection Timeout:获取连接的超时次数。
检查数据库操作代码
代码审查重点:
- 是否关闭连接:确保每次数据库操作后调用 connection.close()。
- 是否使用事务:事务未提交或回滚会导致连接被锁定。
- 是否复用连接:避免在循环中频繁创建新连接。
问题代码:
@Resource
private PlatformTransactionManager transactionManager;
public void queryData() {
TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
// 代码异常,导致没有提交与回滚
....
try {
} catch (Exception e) {
transactionManager.rollback(transaction);
} finally {
if (Objects.nonNull(transaction) && transaction.isNewTransaction() && !transaction.isCompleted()) {
transactionManager.commit(transaction);
}
}
}分析慢查询日志
MySQL 慢查询日志:
SHOW VARIABLES LIKE 'slow_query_log';
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 执行时间超过 1 秒的查询记录日志排查连接泄漏
工具辅助:
- Arthas:通过 trace 命令定位未释放连接的代码路径。
- JVM 堆栈分析:通过 jstack 分析线程堆栈,查看连接被哪些线程占用。
示例命令:
jstack <pid> | grep "HikariPool" -A 20 # 查找连接池相关线程其他同类问题
线程阻塞 / 死锁 / 活锁
现象: 请求卡死无响应,线程数居高不下,CPU 可能不高但吞吐为零或极低。
排查思路:
jstack 分析线程状态:
BLOCKED (on object monitor): 明显锁竞争。查找持有锁的线程和等待锁的线程堆栈,定位竞争资源。
WAITING (parking, on condition, on monitor) / TIMED_WAITING: 等待条件满足(如 Object.wait(), Condition.await())或 I/O 操作。检查是否条件未唤醒或 I/O 超时。
死锁检测: jstack 输出末尾通常会报告 Found one Java-level deadlock,列出死锁线程和锁信息。分析相关代码的同步逻辑。
检查锁使用: 是否过度使用 synchronized 或粗粒度锁?是否该用并发集合 (ConcurrentHashMap) 或更高级的锁 (StampedLock, ReadWriteLock)?
资源等待: 数据库连接池耗尽、HTTP 连接池耗尽、文件句柄耗尽等也会导致线程阻塞。检查相关资源池状态。
注意
数据库连接池耗尽、HTTP 连接池耗尽、文件句柄耗尽等也会导致线程阻塞,需要检查相关资源池状态。
总结与建议
总结
DB 连接池耗尽问题的核心在于 连接资源管理不当,需从配置、代码、数据库优化和监控四个方面综合解决。
建议
- 合理配置连接池参数:根据业务负载动态调整 maximumPoolSize 和超时时间。
- 严格遵循编码规范:确保每次数据库操作后关闭连接,避免泄漏。
- 定期分析慢查询日志:优化高频 SQL,减少数据库负载。
- 引入监控与告警:实时掌握连接池状态,及时发现潜在问题。