Java面试要点:数据库连接池耗尽、慢事务与连接泄漏
前言
数据库连接池耗尽经常表现为接口超时,但根因可能不在连接池本身。它可能由慢 SQL、大事务、连接泄漏、线程池堆积、数据库锁等待或下游变慢引起。
典型现象
- 日志出现
Connection is not available、Timeout waiting for idle object。 - HikariCP active 连接接近 maximumPoolSize。
- 接口大量超时。
- 数据库 QPS 下降但连接数很高。
- Tomcat 线程阻塞等待数据库连接。
故障链路
flowchart TD
A[慢SQL或慢事务] --> B[连接占用时间变长]
B --> C[连接池active打满]
C --> D[请求线程等待连接]
D --> E[Tomcat线程堆积]
E --> F[接口整体变慢]
F --> G[重试增加]
G --> C
排查步骤
看连接池指标
重点指标:
- active 连接数。
- idle 连接数。
- pending 等待线程数。
- connection timeout 次数。
- 连接获取耗时。
如果 active 高、pending 高,说明连接被长期占用。下一步要看谁占用了连接。
看数据库会话
MySQL 常用:
1 | SHOW FULL PROCESSLIST; |
重点看:
- 长时间 running 的 SQL。
- Sleep 时间异常长的连接。
- 等锁的事务。
- 是否有批量更新或大查询。
看慢事务
连接不释放常见于事务时间太长:
- 事务里调用第三方接口。
- 事务里处理大批量数据。
- 事务里执行复杂查询。
- 事务开启后做大量非数据库逻辑。
- 异常分支未正确结束。
事务应该只包住必须保证一致性的数据库操作,不应该包住远程调用、文件处理和复杂计算。
看连接泄漏
如果使用 MyBatis、Spring JdbcTemplate、JPA,一般连接会由框架释放。但以下情况仍可能泄漏:
- 手写 JDBC 未关闭连接。
- 自己管理事务。
- 多数据源切换异常。
- 流式查询 ResultSet 未关闭。
- 异步线程中使用事务上下文不当。
HikariCP 可配置 leakDetectionThreshold 辅助发现连接泄漏,但线上要合理设置,避免误报。
常见处理方案
| 问题 | 临时处理 | 长期治理 |
|---|---|---|
| 慢 SQL | 加限流、杀异常 SQL | 索引优化、SQL 改写 |
| 慢事务 | 暂停任务、回滚版本 | 缩小事务范围 |
| 连接泄漏 | 重启实例 | 修复连接关闭逻辑 |
| 连接池太小 | 临时调大 | 按容量压测配置 |
| DB 锁等待 | 找阻塞事务 | 优化更新顺序和索引 |
参数配置思路
连接池不是越大越好。连接太多会增加数据库上下文切换和锁竞争。
配置时考虑:
- 应用实例数。
- 每实例最大并发。
- 数据库最大连接数。
- SQL 平均耗时和 P99。
- 是否有读写分离。
- 是否存在批处理任务。
例如数据库最大连接数 1000,应用 10 个实例,不能每个实例都配置 200 个连接。还要给运维、迁移、监控和其他服务留余量。
生产止血
- 对慢接口限流。
- 暂停批处理和报表任务。
- 杀掉异常长事务。
- 临时扩容只读实例或应用实例。
- 降低重试次数。
- 回滚最近发布。
如果连接池已经耗尽,不要盲目重试。重试会增加排队请求,进一步拖垮系统。
高频面试题
连接池耗尽怎么排查?
先看连接池 active、idle、pending 和获取连接耗时,再看数据库 processlist、慢 SQL、锁等待和长事务,最后结合线程栈判断请求是否大量阻塞在获取连接。
连接池调大能解决问题吗?
不一定。如果根因是慢 SQL 或连接泄漏,调大只会延迟故障并增加数据库压力。应该先找到连接被谁占用。
事务为什么不能包远程调用?
远程调用不可控,可能超时或重试,会导致数据库连接和锁长时间占用,放大连接池耗尽和锁等待风险。
总结
连接池耗尽是典型连锁故障。面试回答要讲清楚连接池、线程池、数据库锁、慢 SQL 和事务范围之间的关系。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Junly博客!
评论


