Java数据库连接池优化
数据库连接池复用连接,避免频繁创建销毁,是高性能系统的必备组件。
为什么需要连接池
Java
无连接池:
每次请求 → 创建连接 → 执行SQL → 关闭连接
(创建连接耗时100-300ms,开销大)
有连接池:
初始化 → 创建N个连接放入池
每次请求 → 从池获取 → 执行SQL → 归还池
(获取连接耗时<1ms,高效)
主流连接池对比
| 连接池 | 特点 | 适用场景 |
|---|---|---|
| HikariCP | 性能最高,SpringBoot默认 | 生产首选 |
| Druid | 监控丰富,阿里巴巴开源 | 需监控场景 |
| C3P0 | 稳定但性能较低 | 老项目维护 |
| Tomcat JDBC | Tomcat内置 | Tomcat应用 |
HikariCP配置
核心参数
YAML
HikariConfig config = new HikariConfig();
// 必需参数
config.setJdbcUrl("jdbc:mysql://localhost:3306/db");
config.setUsername("user");
config.setPassword("password");
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池大小
config.setMaximumPoolSize(20); // 最大连接数
config.setMinimumIdle(10); // 最小空闲连接
// 连接生命周期
config.setIdleTimeout(600000); // 空闲超时10分钟
config.setMaxLifetime(1800000); // 连接最长存活30分钟
config.setConnectionTimeout(30000); // 获取连接超时30秒
// 验证配置
config.setConnectionTestQuery("SELECT 1"); // 验证SQL
config.setValidationTimeout(5000); // 验证超时
HikariDataSource dataSource = new HikariDataSource(config);
Spring Boot配置
Java
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
maximum-pool-size: 20
minimum-idle: 10
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 30000
pool-name: MyHikariPool
Druid配置
核心参数
Java
DruidDataSource dataSource = new DruidDataSource();
// 基础配置
dataSource.setUrl("jdbc:mysql://localhost:3306/db");
dataSource.setUsername("user");
dataSource.setPassword("password");
// 连接池大小
dataSource.setInitialSize(10); // 初始连接数
dataSource.setMinIdle(10); // 最小空闲
dataSource.setMaxActive(20); // 最大活跃
// 连接生命周期
dataSource.setMaxWait(60000); // 获取连接最大等待
dataSource.setTimeBetweenEvictionRunsMillis(60000); // 检测间隔
dataSource.setMinEvictableIdleTimeMillis(300000); // 最小空闲时间
// 验证配置
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestWhileIdle(true); // 空闲时验证
dataSource.setTestOnBorrow(false); // 获取时验证(影响性能)
dataSource.setTestOnReturn(false); // 归还时验证
// 监控配置
dataSource.setFilters("stat,wall,log4j"); // 监控过滤器
Druid监控
Java
// Web监控配置
@Bean
public ServletRegistrationBean druidStatViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(
new StatViewServlet(), "/druid/*");
bean.addInitParameter("loginUsername", "admin");
bean.addInitParameter("loginPassword", "admin");
return bean;
}
访问 /druid/index.html 可查看:
- SQL执行统计
- 连接池状态
- URI监控
- Spring监控
连接池大小计算
经典公式
Java
连接数 = (核心数 * 2) + 有效磁盘数
示例:4核CPU + 1磁盘
连接数 = 4 * 2 + 1 = 9
实际考虑因素
| 因素 | 影响 |
|---|---|
| 并发用户数 | 用户越多,连接越多 |
| 事务时长 | 事务越长,连接占用越长 |
| 等待时间 | 查询越慢,连接占用越长 |
| CPU核数 | 核数越多,可处理越多 |
推荐配置
Java
// 小型应用(<100并发)
maximumPoolSize: 10-20
// 中型应用(100-500并发)
maximumPoolSize: 20-50
// 大型应用(>500并发)
maximumPoolSize: 50-100(需配合数据库优化)
常见问题与解决
连接泄漏
Java
// 问题:获取连接未归还
Connection conn = dataSource.getConnection();
// 执行SQL后未关闭conn
// 解决:try-finally确保关闭
Connection conn = null;
try {
conn = dataSource.getConnection();
// 执行SQL
} finally {
if (conn != null) {
conn.close(); // 归还连接池
}
}
// 或使用try-with-resources
try (Connection conn = dataSource.getConnection()) {
// 执行SQL
}
连接超时
Java
// 问题:connectionTimeout报错
// 原因:连接池太小或获取连接慢
// 解决
1. 增大maximumPoolSize
2. 增大connectionTimeout
3. 检查数据库连接慢的原因
连接断开
Java
// 问题:连接长时间空闲被数据库断开
// 解决:空闲时验证连接有效性
config.setTestWhileIdle(true);
config.setValidationQuery("SELECT 1");
config.setTimeBetweenEvictionRunsMillis(60000);
监控指标
| 指标 | 说明 | 标准 |
|---|---|---|
| ActiveConnections | 活跃连接数 | < maxPoolSize |
| IdleConnections | 空闲连接数 | > minIdle |
| ThreadsAwaitingConnection | 等待线程数 | 应为0 |
| ConnectionCreationTime | 创建时间 | 应<100ms |
| ConnectionAcquireTime | 获取时间 | 应<10ms |
Java
// HikariCP监控
HikariPoolMXBean pool = dataSource.getHikariPoolMXBean();
System.out.println("活跃连接: " + pool.getActiveConnections());
System.out.println("空闲连接: " + pool.getIdleConnections());
System.out.println("等待线程: " + pool.getThreadsAwaitingConnection());
优化策略
1. 合理设置大小
Java
// 太小:等待连接,性能差
maximumPoolSize: 5 // 并发100时不够
// 太大:数据库压力大,浪费资源
maximumPoolSize: 100 // 数据库连接上限可能不够
// 合理:根据并发和数据库能力
maximumPoolSize: 20-30 // 中等应用
2. 减少连接持有时间
text
// 不推荐:连接持有时间长
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
// 执行很多SQL...
ResultSet rs = stmt.executeQuery(sql1);
// 处理结果...
rs = stmt.executeQuery(sql2);
// 处理结果...
conn.close();
// 推荐:快速获取,快速归还
try (Connection conn = dataSource.getConnection()) {
// 只执行必要SQL,快速归还
}
3. 使用连接池监控
text
// Druid内置监控
// HikariCP配合Micrometer/Prometheus
@Bean
public MeterRegistry meterRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
// 指标暴露到Prometheus
// Grafana可视化监控
注意事项
maximumPoolSize不是越大越好,需配合数据库能力
连接获取后必须关闭(归还)
空闲连接验证避免使用断开的连接
监控连接池状态,及时发现问题
生产环境建议使用HikariCP,性能最优
要点总结
- 连接池复用连接,避免频繁创建开销
- HikariCP性能最高,Spring Boot默认使用
- Druid监控丰富,适合需要监控的场景
- 连接池大小计算:(CPU核数 * 2) + 磁盘数
- 获取连接后必须close()归还,防止泄漏
📝 发现内容有误?点击此处直接编辑