2025-06-27
实战设计
0

目录

问题背景
排查思路与实战分析
确认问题表现
初步定位原因
详细排查步骤
检查连接池配置
分析连接池使用情况
检查数据库操作代码
分析慢查询日志
排查连接泄漏
其他同类问题
线程阻塞 / 死锁 / 活锁
排查思路:
总结与建议
总结
建议

某个系统上线后出现连接池耗尽,如何一步步排查。可能还需要引用知识库中的具体配置示例,比如HikariCP的配置参数,或者Druid的监控方法。

遇到了线上连接池耗尽的问题,想要了解如何快速定位和解决。

问题背景

某核心业务系统上线后,用户反馈访问缓慢,部分接口出现超时甚至报错。通过监控平台查看,发现服务端异常日志中频繁出现如下错误:

text
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 配置示例:

yaml
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()。
  • 是否使用事务:事务未提交或回滚会导致连接被锁定。
  • 是否复用连接:避免在循环中频繁创建新连接。

问题代码:

java
@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 慢查询日志:

sql
SHOW VARIABLES LIKE 'slow_query_log'; SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; -- 执行时间超过 1 秒的查询记录日志

排查连接泄漏

工具辅助:

  • Arthas:通过 trace 命令定位未释放连接的代码路径。
  • JVM 堆栈分析:通过 jstack 分析线程堆栈,查看连接被哪些线程占用。

示例命令:

sh
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,减少数据库负载。
  • 引入监控与告警:实时掌握连接池状态,及时发现潜在问题。

本文作者:柳始恭

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!