HikariCP 连接池
为什么需要连接池?
没有连接池的问题
// 每次请求都创建和关闭连接
public User getUser(int id) {
Connection conn = DriverManager.getConnection(url, user, password); // 耗时操作
// ... 执行查询
conn.close(); // 耗时操作
}问题:
- 创建连接开销大:TCP 握手、SSL 认证、权限验证
- 连接数不可控:并发高峰可能耗尽数据库连接
- 资源浪费:频繁创建销毁连接
连接池的作用
应用程序 连接池 数据库
│ │ │
│── 请求连接 ─────────────→│ │
│ │── 复用已建立连接 ───────→│
│←──────── 返回连接 ──────│ │
│ │ │
│── 归还连接 ─────────────→│←── 连接保持 ───────────│
│ │ │优势:
- 复用连接,减少创建开销
- 控制最大连接数,保护数据库
- 提供连接健康检查
HikariCP 简介
为什么 HikariCP 最快?
| 优化点 | 说明 |
|---|---|
| 字节码级别优化 | 使用 Javassist 生成代理类 |
| ConcurrentBag | 自定义并发容器,减少锁竞争 |
| FastList | 优化的 ArrayList,减少范围检查 |
| 代理优化 | 精简的 Connection 代理实现 |
性能对比
| 连接池 | 获取连接耗时 | 吞吐量 |
|---|---|---|
| HikariCP | ~0.1ms | 最高 |
| Druid | ~0.5ms | 高 |
| C3P0 | ~2ms | 中 |
| DBCP | ~3ms | 低 |
核心配置参数
基础配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
# 连接池名称
pool-name: MyHikariPool
# 最小空闲连接数
minimum-idle: 5
# 最大连接数
maximum-pool-size: 20
# 连接超时时间(毫秒)
connection-timeout: 30000
# 空闲连接存活最大时间(毫秒)
idle-timeout: 600000
# 连接最大存活时间(毫秒)
max-lifetime: 1800000
# 连接测试查询
connection-test-query: SELECT 1参数详解
| 参数 | 默认值 | 说明 |
|---|---|---|
maximum-pool-size | 10 | 最大连接数 |
minimum-idle | 同 maximum | 最小空闲连接数 |
connection-timeout | 30s | 等待连接的超时时间 |
idle-timeout | 10min | 空闲连接最大存活时间 |
max-lifetime | 30min | 连接最大存活时间 |
connection-test-query | 无 | 连接有效性测试 SQL |
leak-detection-threshold | 0 | 连接泄露检测阈值 |
最大连接数计算公式
最大连接数 = (核心数 * 2) + 有效磁盘数
例如:8核CPU + 1块磁盘 = (8 * 2) + 1 = 17原因:
- 计算密集型任务:核心数 + 1
- I/O 密集型任务:核心数 * 2
- 数据库操作是 I/O 密集型
实际配置: 根据业务压力测试调整,不是越大越好。
连接池工作原理
连接获取流程
应用程序请求连接
↓
检查空闲连接列表
↓
有可用连接? → 是 → 验证连接有效性 → 返回连接
↓ 否
当前连接数 < 最大值?
↓ 是 ↓ 否
创建新连接 等待其他连接释放
↓ ↓
返回连接 超时? → 抛异常连接归还流程
应用程序归还连接
↓
重置连接状态(回滚事务、清除警告等)
↓
当前空闲连接数 < 最小空闲数?
↓ 是 ↓ 否
放入空闲池 关闭连接连接泄露检测
问题场景
// 连接泄露:忘记关闭连接
public void leak() {
Connection conn = dataSource.getConnection();
// 业务逻辑...
// 忘记 conn.close()
}检测配置
spring:
datasource:
hikari:
# 连接泄露检测阈值(毫秒)
# 连接借出超过此时间未归还,记录警告日志
leak-detection-threshold: 60000 # 60秒日志输出
警告 - Connection leak detection triggered for connection xxx
Connection has been out of pool for 60001ms常见问题
1. ConnectionTimeoutException
Caused by: java.sql.SQLTransientConnectionException:
HikariPool - Connection is not available, request timed out after 30000ms原因:
- 连接池太小
- 连接泄露
- 查询太慢
解决:
# 增大连接池
maximum-pool-size: 30
# 增加超时时间
connection-timeout: 60000
# 检查泄露
leak-detection-threshold: 300002. 连接池预热
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
// ... 配置参数
// 预热连接池
ds.getConnection().close(); // 触发初始化
return ds;
}
}3. MySQL 连接断开
MySQL 默认 wait_timeout=8小时,连接空闲超过 8 小时会断开。
解决:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?autoReconnect=true
hikari:
# 连接最大存活时间小于 MySQL wait_timeout
max-lifetime: 1800000 # 30分钟
# 定期测试连接
connection-test-query: SELECT 1
# 或使用 MySQL 驱动的自动重连
# autoReconnect=true(已废弃,不推荐)监控指标
JMX 监控
spring:
datasource:
hikari:
register-mbeans: true使用 JConsole 或 VisualVM 连接查看:
totalConnections:总连接数activeConnections:活跃连接数idleConnections:空闲连接数threadsAwaitingConnection:等待连接的线程数
Prometheus + Micrometer
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>// 暴露 HikariCP 指标
@Bean
public MeterBinder hikariMetrics(HikariDataSource dataSource) {
return new HikariDataSourceMetrics(dataSource, "my-pool", Collections.emptyList());
}关键指标:
hikaricp_connections_active:活跃连接数hikaricp_connections_idle:空闲连接数hikaricp_connections_pending:等待获取连接的线程数hikaricp_connections_creation_seconds:连接创建耗时
与其他连接池对比
| 特性 | HikariCP | Druid | C3P0 |
|---|---|---|---|
| 性能 | 最高 | 高 | 中 |
| 监控 | 基础 | 丰富(内置监控页面) | 基础 |
| SQL 监控 | 无 | 有 | 无 |
| 配置复杂度 | 简单 | 中等 | 复杂 |
| Spring Boot 默认 | ✅ | ❌ | ❌ |
选择建议:
- HikariCP:追求性能,Spring Boot 默认
- Druid:需要 SQL 监控、防注入等功能
最佳实践配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
pool-name: MyAppPool
# 根据实际负载调整
maximum-pool-size: 20
minimum-idle: 5
# 超时配置
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
# 泄露检测(生产环境建议开启)
leak-detection-threshold: 60000
# 连接验证
validation-timeout: 5000面试高频问题
Q1: HikariCP 为什么比其他连接池快?
- 字节码优化:Javassist 生成代理类,减少反射调用
- ConcurrentBag:自定义并发容器,无锁设计
- 精简实现:代码量少,减少不必要的功能
- FastList:优化的集合,减少边界检查
Q2: 连接池最大连接数如何设置?
公式:(CPU核心数 * 2) + 磁盘数
实际:需要结合业务压测调整
- 太大:数据库压力大,上下文切换开销
- 太小:并发请求排队等待Q3: 连接泄露如何排查?
- 开启泄露检测:
leak-detection-threshold - 查看日志定位泄露代码
- 检查未关闭连接的代码
- 使用 try-with-resources 确保关闭
Q4: HikariCP 和 Druid 如何选择?
| 场景 | 推荐 |
|---|---|
| 追求性能 | HikariCP |
| 需要 SQL 监控 | Druid |
| Spring Boot 项目 | HikariCP(默认) |
| 防止 SQL 注入 | Druid(内置 wall filter) |
总结
HikariCP 核心要点:
1. 性能最优:字节码优化 + ConcurrentBag
2. 关键配置:maximum-pool-size、connection-timeout
3. 泄露检测:leak-detection-threshold
4. 连接数公式:(核心数 * 2) + 磁盘数
5. 监控指标:活跃连接、空闲连接、等待线程