引言:为什么需要连接池?
数据库连接是昂贵的资源。每次应用需要访问数据库时,都需要建立连接,这个过程涉及网络通信、认证等开销。使用连接池可以复用已建立的连接,大大提高性能。
连接池类比:共享单车
没有连接池:每次出门都买一辆新车
- 每次都需要花钱(建立连接)
- 用完就扔(关闭连接)
- 非常浪费
有连接池:使用共享单车
- 预先准备一些车(初始化连接)
- 需要时扫码骑车(获取连接)
- 骑完锁车还回去(归还连接)
- 车可以给别人用(复用)
- 省时省力!
连接池 vs 传统方式
传统方式(每次新建连接)
// 每次请求都创建新连接
for (int i = 0; i < 1000; i++) {
Connection conn = DriverManager.getConnection(url, user, password);
// 执行SQL
conn.close(); // 用完就关闭
}
// 问题:创建1000次连接,非常慢!
连接池方式(复用连接)
// 从连接池获取连接
DataSource dataSource = new HikariDataSource(config);
for (int i = 0; i < 1000; i++) {
Connection conn = dataSource.getConnection(); // 从池中获取
// 执行SQL
conn.close(); // 归还到池中,不是真正关闭
}
// 只需创建少量连接,复用1000次!
常用连接池对比
| 连接池 | 性能 | 功能 | 维护 | 适用场景 |
|---|---|---|---|---|
| HikariCP | 最高 | 基础 | 活跃 | 高性能需求,Spring Boot默认 |
| Druid | 中高 | 丰富(监控、过滤) | 活跃(阿里巴巴) | 需要监控功能 |
| C3P0 | 较低 | 丰富 | 较少 | 老项目兼容 |
| DBCP | 较低 | 基础 | 较少 | Apache项目 |
HikariCP(推荐)
HikariCP是Spring Boot 2.0+的默认连接池,以高性能著称。
Maven依赖
com.zaxxer
HikariCP
4.0.3
基本配置
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池大小
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接
// 连接超时
config.setConnectionTimeout(30000); // 获取连接超时(毫秒)
config.setIdleTimeout(600000); // 空闲连接超时
config.setMaxLifetime(1800000); // 连接最大生命周期
DataSource dataSource = new HikariDataSource(config);
// 使用
Connection conn = dataSource.getConnection();
try {
// 执行SQL
} finally {
conn.close(); // 归还到连接池
}
HikariCP配置参数详解
| 参数 | 说明 | 默认值 |
|---|---|---|
| maximumPoolSize | 最大连接数 | 10 |
| minimumIdle | 最小空闲连接 | 等于maximumPoolSize |
| connectionTimeout | 获取连接超时 | 30000ms |
| idleTimeout | 空闲连接存活时间 | 600000ms |
| maxLifetime | 连接最大生命周期 | 1800000ms |
| poolName | 连接池名称 | HikariPool-1 |
Druid(阿里巴巴)
Druid提供强大的监控和SQL过滤功能。
Maven依赖
com.alibaba
druid
1.2.16
基本配置
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池大小
dataSource.setInitialSize(5); // 初始连接数
dataSource.setMaxActive(20); // 最大连接数
dataSource.setMinIdle(5); // 最小空闲
// 连接验证
dataSource.setValidationQuery("SELECT 1"); // 验证SQL
dataSource.setTestWhileIdle(true); // 空闲时验证
dataSource.setTestOnBorrow(false); // 借用时验证
dataSource.setTestOnReturn(false); // 归还时验证
// 连接超时
dataSource.setMaxWait(60000); // 获取连接最大等待时间
// 启用监控统计
dataSource.setFilters("stat,wall");
Druid监控配置
// Web监控页面
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
// 监控过滤
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg");
// 通过 http://localhost:8080/druid 访问监控页面
C3P0(老牌连接池)
com.mchange
c3p0
0.9.5.5
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.cj.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
cpds.setUser("root");
cpds.setPassword("password");
cpds.setMinPoolSize(5); // 最小连接数
cpds.setMaxPoolSize(20); // 最大连接数
cpds.setAcquireIncrement(3); // 获取新连接时增长数
// 连接验证
cpds.setTestConnectionOnCheckout(true); // 归还时验证
cpds.setIdleConnectionTestPeriod(300); // 空闲测试周期(秒)
DBCP(Apache Commons)
org.apache.commons
commons-dbcp2
2.9.0
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setInitialSize(5); // 初始连接数
dataSource.setMaxTotal(20); // 最大连接数
dataSource.setMaxIdle(10); // 最大空闲
dataSource.setMinIdle(5); // 最小空闲
// 连接验证
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);
Spring Boot配置
application.yml配置HikariCP(默认)
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
# HikariCP配置
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
pool-name: MyHikariPool
切换到Druid
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 5
max-active: 20
min-idle: 5
stat-view-enabled: true # 开启监控
连接池参数调优
如何设置连接数?
// 公式:连接数 = ((核心数 * 2) + 磁盘数)
// 例如:4核CPU,1个SSD -> 最佳连接数约 9-10
// 考虑因素:
// 1. 数据库服务器配置
// 2. 应用服务器配置
// 3. 业务特性(长事务还是短事务)
参数设置建议
maximumPoolSize:不要设置过大,通常10-20足够
minimumIdle:设置为maximumPoolSize的一半左右
connectionTimeout:根据业务容忍度设置,通常30秒
idleTimeout:根据数据库服务器超时设置
连接池使用示例
public class UserDAO {
private DataSource dataSource;
public UserDAO(DataSource dataSource) {
this.dataSource = dataSource;
}
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setLong(1, id);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
return user;
}
} catch (SQLException e) {
throw new RuntimeException("查询用户失败", e);
}
return null;
}
public void insert(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, user.getName());
pstmt.setString(2, user.getEmail());
pstmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("插入用户失败", e);
}
}
}
连接池注意事项
常见问题:
- 连接泄露:获取连接后忘记close(),导致连接耗尽
- 连接超时:maxWait设置过短,高并发时等待超时
- 连接池过大:占用过多数据库连接,影响数据库性能
小结
- 连接池复用连接,避免频繁创建/关闭连接的开销
- HikariCP:性能最高,Spring Boot默认推荐
- Druid:功能丰富,支持监控和SQL过滤
- C3P0/DBCP:老牌连接池,现在较少使用
- 连接数不是越大越好,通常10-20足够
- 使用try-with-resources确保连接正确归还