引言

数据库连接池是提高应用性能的关键。本文介绍连接池的原理和最佳实践。

一、连接池原理

1
2
3
4
5
6
7
应用 → 连接池 → 数据库

连接池组件:
1. 连接管理:创建、复用、销毁
2. 空闲队列:存放空闲连接
3. 活跃队列:存放使用中的连接
4. 参数配置:最小/最大连接数、超时时间

二、HikariCP配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver

hikari:
# 最小空闲连接数
minimum-idle: 5
# 最大连接数
maximum-pool-size: 20
# 连接超时时间(毫秒)
connection-timeout: 30000
# 空闲连接最大存活时间(毫秒)
idle-timeout: 600000
# 连接最大生命周期(毫秒)
max-lifetime: 1800000
# 连接测试查询
connection-test-query: SELECT 1

三、Druid配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Configuration
public class DruidConfig {
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");

// 初始化连接数
dataSource.setInitialSize(5);
// 最小空闲连接数
dataSource.setMinIdle(5);
// 最大活跃连接数
dataSource.setMaxActive(20);
// 获取连接超时时间
dataSource.setMaxWait(60000);

// 连接有效性检测
dataSource.setTestWhileIdle(true);
dataSource.setTestOnBorrow(false);
dataSource.setTestOnReturn(false);
dataSource.setValidationQuery("SELECT 1");

// 配置间隔多久检测一次空闲连接
dataSource.setTimeBetweenEvictionRunsMillis(60000);
// 配置连接最小生存时间
dataSource.setMinEvictableIdleTimeMillis(300000);

// 配置监控统计
try {
dataSource.setFilters("stat,wall");
} catch (SQLException e) {
e.printStackTrace();
}

return dataSource;
}
}

四、连接池监控

1
2
3
4
5
6
7
8
9
10
11
12
13
// Druid监控
@Bean
public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
ServletRegistrationBean<StatViewServlet> bean =
new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");

Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin");
initParams.put("loginPassword", "123456");
bean.setInitParameters(initParams);

return bean;
}

五、最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 正确使用连接
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
ps.setLong(1, userId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
// 处理结果
}
}
} catch (SQLException e) {
e.printStackTrace();
}

// 错误示例:未关闭连接
Connection conn = dataSource.getConnection();
// 使用连接...
// 忘记关闭,导致连接泄露

六、参数调优

1
2
3
4
5
6
7
8
9
10
11
1. 最大连接数 = (核心数 * 2) + 有效磁盘数
2. 最小连接数 = 最大连接数 / 2
3. 连接超时时间:30秒
4. 空闲超时时间:10分钟
5. 最大生命周期:30分钟

调优指标:
- 连接获取时间
- 连接使用率
- 连接等待数
- 连接泄露数

总结

数据库连接池核心:

  1. 复用连接:避免频繁创建销毁
  2. 参数调优:根据业务调整
  3. 监控告警:及时发现问题
  4. 正确使用:避免连接泄露

参考资料