跳到主要内容

数据库连接问题

问题

线上出现 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 为什么比其他连接池快?

答案

  1. 字节码精简:编译后的代码量极少
  2. ConcurrentBag:自定义无锁并发数据结构
  3. FastList:替代 ArrayList,省去范围检查
  4. 代理优化:用 Javassist 生成代理类替代 JDK 动态代理
  5. 默认不做连接测试(避免 SELECT 1 开销)

Q2: 连接池大小设多少合适?

答案

  • 不要设太大!20 个连接可处理 3000+ TPS
  • 过多连接 → 数据库线程上下文切换多 → 性能反而下降
  • 参考 PostgreSQL 文档:connections = (core_count * 2) + spindle_count
  • 以压测为准

Q3: MyBatis 的连接什么时候释放?

答案

Spring 管理的事务中,连接在事务提交/回滚后释放。无事务时,每次 SqlSession 操作获取和释放连接。

@Transactional 方法内多次 SQL 共享同一个连接,方法结束后归还连接池。

相关链接