数据库连接问题
问题
线上出现 Cannot get a connection, pool error Timeout waiting for idle object 或大量连接泄漏,如何排查?
答案
常见连接问题
| 问题 | 表现 | 原因 |
|---|---|---|
| 获取连接超时 | Timeout waiting for idle object | 连接池打满 |
| 连接泄漏 | 活跃连接持续增长不释放 | 未关闭连接 |
| 慢查询阻塞 | 连接被长时间占用 | SQL 慢或锁等待 |
| 连接被 MySQL 关闭 | Communications link failure | 连接空闲超时 |
连接池打满排查
HikariCP 关键配置
HikariCP 推荐配置
spring:
datasource:
hikari:
# 连接池大小(核心参数)
maximum-pool-size: 20 # 最大连接数
minimum-idle: 5 # 最小空闲连接
# 超时设置
connection-timeout: 3000 # 获取连接超时 3s(默认 30s 太长)
idle-timeout: 600000 # 空闲连接最大存活 10min
max-lifetime: 1800000 # 连接最大生命周期 30min(需小于 MySQL wait_timeout)
# 泄漏检测(关键!)
leak-detection-threshold: 60000 # 连接超过 60s 未归还视为泄漏
连接池大小建议
官方建议公式:connections = (core_count * 2) + effective_spindle_count
一般 8 核服务器配 10-20 个连接足够。连接池不是越大越好——过多连接增加数据库上下文切换开销。
连接泄漏排查
开启 leak-detection-threshold 后,HikariCP 会在日志中打印泄漏警告和堆栈:
泄漏检测日志
WARN HikariPool-1 - Connection leak detection triggered for conn0
java.lang.Exception: Apparent connection leak detected
at com.example.service.OrderService.export(OrderService.java:45)
at ...
❌ 连接泄漏代码
public void export() {
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM orders");
// 处理结果...
// 异常时 conn 不会关闭 → 泄漏
conn.close();
}
✅ 使用 try-with-resources
public void export() {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM orders")) {
// 处理结果
} // 自动关闭,即使异常也不泄漏
}
MySQL 连接空闲断开
解决连接被 MySQL 关闭
spring:
datasource:
hikari:
# max-lifetime 必须比 MySQL 的 wait_timeout 小
# MySQL 默认 wait_timeout = 28800 (8小时)
max-lifetime: 1800000 # 30 分钟
# 连接有效性检测
connection-test-query: SELECT 1
常见面试问题
Q1: HikariCP 为什么比其他连接池快?
答案:
- 字节码精简:编译后的代码量极少
- ConcurrentBag:自定义无锁并发数据结构
- FastList:替代 ArrayList,省去范围检查
- 代理优化:用 Javassist 生成代理类替代 JDK 动态代理
- 默认不做连接测试(避免
SELECT 1开销)
Q2: 连接池大小设多少合适?
答案:
- 不要设太大!20 个连接可处理 3000+ TPS
- 过多连接 → 数据库线程上下文切换多 → 性能反而下降
- 参考 PostgreSQL 文档:
connections = (core_count * 2) + spindle_count - 以压测为准
Q3: MyBatis 的连接什么时候释放?
答案:
Spring 管理的事务中,连接在事务提交/回滚后释放。无事务时,每次 SqlSession 操作获取和释放连接。
@Transactional 方法内多次 SQL 共享同一个连接,方法结束后归还连接池。