一、 数据库连接池基础概念
1.1 什么是数据库连接池?
数据库连接池(Database Connection Pool)是一种管理数据库连接的技术,它在应用程序启动时创建一定数量的数据库连接,并将这些连接保存在"池"中。当应用程序需要与数据库交互时,直接从池中获取连接,使用完毕后将连接归还给池而不是真正关闭。
生活化比喻:想象连接池就像一个共享单车停放点。早上上班时(应用启动),停放点会准备好一定数量的单车(数据库连接)。当有人需要骑车(访问数据库)时,直接从停放点取用,用完后放回停放点供其他人使用,而不是每次需要时都去买一辆新车(创建新连接),用完后直接扔掉(关闭连接)。
1.2 为什么需要连接池?
无连接池的问题 | 连接池解决方案 |
---|---|
每次访问数据库都要创建新连接,耗时 | 连接已预先创建,随取随用 |
频繁创建/关闭连接消耗系统资源 | 连接复用,减少系统开销 |
难以控制连接数量,可能导致数据库过载 | 可配置最大连接数,保护数据库 |
连接泄漏风险高 | 完善的连接回收机制 |
1.3 主流Java连接池对比
特性 | HikariCP | Tomcat JDBC | Commons DBCP2 | Druid |
---|---|---|---|---|
性能 | 极高 | 高 | 中等 | 高 |
稳定性 | 高 | 高 | 中等 | 高 |
监控 | 基本 | 基本 | 基本 | 全面 |
功能 | 核心功能 | 核心功能 | 核心功能 | 扩展功能丰富 |
适用场景 | 高性能Web应用 | Tomcat应用 | 传统应用 | 需要监控的应用 |
维护状态 | 活跃 | 活跃 | 维护 | 活跃 |
Spring Boot默认选择:从Spring Boot 2.0开始,HikariCP成为默认连接池,因其性能卓越。
二、Spring Boot中配置HikariCP连接池
2.1 基础配置
首先在pom.xml
中添加依赖(Spring Boot Starter Data JPA已包含HikariCP):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
基础配置示例(application.properties
):
# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/my_db?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Hikari连接池配置
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.connection-timeout=30000 # 连接超时时间(毫秒)
spring.datasource.hikari.minimum-idle=10 # 最小空闲连接数
spring.datasource.hikari.maximum-pool-size=20 # 最大连接数(包括空闲和使用中的连接)
spring.datasource.hikari.idle-timeout=600000 # 空闲连接超时时间(毫秒)
spring.datasource.hikari.max-lifetime=1800000 # 连接最大存活时间(毫秒)
spring.datasource.hikari.auto-commit=true # 是否自动提交事务
2.2 配置参数详解
核心参数表
参数名 | 默认值 | 说明 |
---|---|---|
connection-timeout | 30000 | 从池中获取连接的最大等待时间(毫秒),超时抛异常 |
minimum-idle | - | 池中维护的最小空闲连接数,建议与maximum-pool-size相同 |
maximum-pool-size | 10 | 池中最大连接数(包括空闲和使用中的连接) |
idle-timeout | 600000 | 连接允许在池中空闲的最长时间(毫秒),超时被回收 |
max-lifetime | 1800000 | 连接的最大存活时间(毫秒),超时被回收,建议比数据库wait_timeout小几秒 |
auto-commit | true | 是否自动提交事务 |
pool-name | - | 连接池名称,用于监控和日志 |
connection-test-query | - | 连接测试查询(SELECT 1等),部分驱动需要 |
initialization-fail-timeout | 1 | 初始化连接池失败时的超时时间(毫秒) |
如何确定合适的连接池大小?
经验公式:
连接池大小 = (核心数 * 2) + 有效磁盘数
例如:4核CPU + 1个磁盘 → (4*2)+1 = 9 → 设置maximum-pool-size=10
实际建议:
- 小型应用:10-20
- 中型应用:20-50
- 大型应用:50-100(需根据数据库服务器配置调整)
2.3 代码示例:获取和使用连接
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
@Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
@Autowired
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 查询所有用户
*/
public List<Map<String, Object>> findAllUsers() {
// 这里JdbcTemplate会自动从连接池获取连接,执行完毕后自动释放
return jdbcTemplate.queryForList("SELECT * FROM users");
}
/**
* 添加新用户
*/
public int addUser(String username, String email) {
// 使用预编译SQL防止SQL注入
String sql = "INSERT INTO users(username, email) VALUES (?, ?)";
// 返回影响的行数
return jdbcTemplate.update(sql, username, email);
}
}
三、高级配置与优化
3.1 多数据源配置
实际项目中可能需要连接多个数据库,下面是配置示例:
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Primary
@Bean(name = "primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate(
@Qualifier("primaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean(name = "secondaryJdbcTemplate")
public JdbcTemplate secondaryJdbcTemplate(
@Qualifier("secondaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
对应application.properties
配置:
# 主数据源
spring.datasource.primary.url=jdbc:mysql://localhost:3306/db1
spring.datasource.primary.username=user1
spring.datasource.primary.password=pass1
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
# 次数据源
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/db2
spring.datasource.secondary.username=user2
spring.datasource.secondary.password=pass2
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
# Hikari配置可以分别设置
spring.datasource.primary.hikari.maximum-pool-size=20
spring.datasource.secondary.hikari.maximum-pool-size=10
3.2 连接池监控
HikariCP自带监控
可以通过HikariDataSource对象获取监控数据:
@RestController
@RequestMapping("/monitor")
public class PoolMonitorController {
@Autowired
private DataSource dataSource;
@GetMapping("/pool-info")
public Map<String, Object> getPoolInfo() {
if (dataSource instanceof HikariDataSource) {
HikariPoolMXBean pool = ((HikariDataSource) dataSource).getHikariPoolMXBean();
Map<String, Object> info = new LinkedHashMap<>();
info.put("activeConnections", pool.getActiveConnections());
info.put("idleConnections", pool.getIdleConnections());
info.put("totalConnections", pool.getTotalConnections());
info.put("threadsAwaitingConnection", pool.getThreadsAwaitingConnection());
info.put("connectionTimeout", ((HikariDataSource) dataSource).getConnectionTimeout());
return info;
}
return Collections.singletonMap("error", "Not a Hikari datasource");
}
}
使用Spring Boot Actuator监控
- 添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置
application.properties
:
# 开启健康端点详细信息
management.endpoint.health.show-details=always
# 开启hikari健康指示器
management.health.db.enabled=true
访问/actuator/health
可查看连接池健康状况。
3.3 性能优化技巧
1. 合理设置连接池大小
# 根据公式 (core_count * 2) + effective_spindle_count
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
2. 适当的超时设置
# 比数据库的wait_timeout小2-3分钟
spring.datasource.hikari.max-lifetime=1740000
# 空闲连接回收时间
spring.datasource.hikari.idle-timeout=600000
# 获取连接超时时间
spring.datasource.hikari.connection-timeout=30000
3. 启用准备好的语句缓存
# MySQL配置
# 开启缓存预编译语句,以提升数据库操作性能
spring.datasource.hikari.data-source-properties.cachePrepStmts=true
# 设置预编译语句的缓存大小为 250
spring.datasource.hikari.data-source-properties.prepStmtCacheSize=250
# 设置预编译语句缓存中 SQL 语句的最大长度为 2048
spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048
# 使用服务器端的预编译语句,增强安全性并提升性能
spring.datasource.hikari.data-source-properties.useServerPrepStmts=true
4. 连接验证配置
# 连接测试查询
spring.datasource.hikari.connection-test-query=SELECT 1
# 连接验证超时(秒)
spring.datasource.hikari.validation-timeout=5000
四、Druid连接池的配置与使用
4.1 Druid简介
Druid是阿里巴巴开源的数据库连接池,除了提供连接池功能外,还提供了强大的监控和扩展功能。
主要特点:
- 高性能
- 强大的监控功能(SQL监控、URI监控等)
- 内置防御SQL注入功能
- 支持多种数据库
- 扩展性强
4.2 配置Druid连接池
- 添加依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
- 基础配置(
application.properties
):
# 基本配置
spring.datasource.url=jdbc:mysql://localhost:3306/my_db
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Druid特定配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 初始化大小
spring.datasource.druid.initial-size=5
# 最小空闲连接数
spring.datasource.druid.min-idle=5
# 最大活跃连接数
spring.datasource.druid.max-active=20
# 获取连接等待超时时间
spring.datasource.druid.max-wait=60000
# 间隔多久检测需要关闭的空闲连接
spring.datasource.druid.time-between-eviction-runs-millis=60000
# 连接保持空闲而不被驱逐的最小时间
spring.datasource.druid.min-evictable-idle-time-millis=300000
# 测试连接是否可用的SQL
spring.datasource.druid.validation-query=SELECT 1
# 申请连接时执行validationQuery检测连接是否有效
spring.datasource.druid.test-on-borrow=false
# 归还连接时执行validationQuery检测连接是否有效
spring.datasource.druid.test-on-return=false
# 是否缓存preparedStatement
spring.datasource.druid.pool-prepared-statements=true
# 每个连接最多缓存多少个preparedStatement
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
4.3 Druid监控配置
@Configuration
public class DruidConfig {
/**
* 配置Druid监控
*/
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServlet() {
ServletRegistrationBean<StatViewServlet> servletRegistrationBean =
new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
// 添加IP白名单
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
servletRegistrationBean.addInitParameter("deny", "192.168.1.100");
// 控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "123456");
// 是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 配置Web监控过滤器
*/
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter() {
FilterRegistrationBean<WebStatFilter> filterRegistrationBean =
new FilterRegistrationBean<>(new WebStatFilter());
// 添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤格式
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
配置完成后,访问http://localhost:8080/druid
可查看Druid监控界面。
4.4 HikariCP与Druid对比
特性 | HikariCP | Druid |
---|---|---|
性能 | 极高 | 高 |
监控功能 | 基本 | 非常全面 |
SQL防注入 | 不支持 | 支持 |
扩展性 | 一般 | 强 |
社区活跃度 | 高 | 高 |
适用场景 | 追求极致性能 | 需要全面监控和防护 |
配置复杂度 | 简单 | 较复杂 |
默认Spring支持 | Spring Boot默认 | 需要额外配置 |
五、连接池常见问题与解决方案
5.1 常见问题排查表
问题现象 | 可能原因 | 解决方案 |
---|---|---|
获取连接超时 | 连接池大小不足 | 增加maximum-pool-size |
连接泄漏 | 检查代码确保连接关闭 | |
数据库连接数过多 | 连接池配置过大 | 合理设置连接池大小 |
连接未正确关闭 | 使用try-with-resources | |
应用启动时连接池初始化失败 | 数据库配置错误 | 检查URL、用户名、密码 |
数据库未启动 | 确保数据库服务已启动 | |
性能下降 | 连接验证频繁 | 调整test-on-borrow等参数 |
未启用预处理语句缓存 | 启用并配置预处理缓存 |
5.2 连接泄漏检测与处理
检测方法:
- 启用连接泄漏检测(HikariCP):
spring.datasource.hikari.leak-detection-threshold=60000 # 60秒
- 监控日志中查找类似警告:
Connection leak detection triggered for connection...
处理方案:
// 正确使用连接的方式
try (Connection connection = dataSource.getConnection()) {
// 使用连接
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
// 处理结果集
} // 自动关闭连接
// 使用JdbcTemplate更安全,无需手动管理连接
jdbcTemplate.query("SELECT * FROM users", (rs, rowNum) -> {
// 处理结果集
return null;
});
5.3 数据库连接池最佳实践
-
始终使用连接池:不要直接创建数据库连接
-
合理配置连接池大小:
- 不是越大越好
- 根据公式和实际测试调整
-
使用预处理语句:
// 好:使用预处理语句 jdbcTemplate.update("INSERT INTO users(name, email) VALUES (?, ?)", name, email); // 不好:拼接SQL jdbcTemplate.update("INSERT INTO users(name, email) VALUES (" + name + ", " + email + ")");
-
及时释放资源:
- 使用try-with-resources
- 或者使用Spring的JdbcTemplate等工具
-
监控连接池:
- 定期检查连接池状态
- 设置合理的告警阈值
-
处理慢查询:
- 监控并优化慢SQL
- 避免长事务占用连接
六、实战案例:电商系统连接池配置
6.1 场景描述
假设我们正在开发一个电商系统,有以下特点:
- 高峰期QPS约500
- 平均SQL执行时间50ms
- 数据库服务器配置:8核CPU,SSD存储
6.2 连接池配置
# HikariCP配置
# 设置 HikariCP 连接池的最大连接数,这里计算方式为 (8 个核心 * 2) + 14 = 30
spring.datasource.hikari.maximum-pool-size=30
# 设置 HikariCP 连接池的最小空闲连接数为 10
spring.datasource.hikari.minimum-idle=10
# 设置获取连接的超时时间为 3000 毫秒(3 秒),超过这个时间获取连接失败
spring.datasource.hikari.connection-timeout=3000
# 设置连接空闲的超时时间为 60000 毫秒(1 分钟),空闲时间超过这个值的连接会被释放
spring.datasource.hikari.idle-timeout=60000
# 设置连接的最大生命周期为 1800000 毫秒(30 分钟),超过这个时间的连接会被强制关闭
spring.datasource.hikari.max-lifetime=1800000
# 设置泄漏检测的阈值为 60000 毫秒(60 秒),如果连接使用时间超过该阈值则可能被认定为泄漏
spring.datasource.hikari.leak-detection-threshold=60000
# MySQL优化参数
# 开启缓存预编译语句,提高数据库操作性能
spring.datasource.hikari.data-source-properties.cachePrepStmts=true
# 设置预编译语句缓存的大小为 250
spring.datasource.hikari.data-source-properties.prepStmtCacheSize=250
# 设置预编译语句缓存中 SQL 语句的最大长度为 2048
spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048
# 使用服务器端的预编译语句,提高安全性和性能
spring.datasource.hikari.data-source-properties.useServerPrepStmts=true
# 使用本地会话状态,有助于提高性能
spring.datasource.hikari.data-source-properties.useLocalSessionState=true
# 重写批处理语句,提高批量操作的性能
spring.datasource.hikari.data-source-properties.rewriteBatchedStatements=true
# 缓存结果集元数据,提高查询性能
spring.datasource.hikari.data-source-properties.cacheResultSetMetadata=true
# 缓存服务器配置信息,减少重复获取
spring.datasource.hikari.data-source-properties.cacheServerConfiguration=true
# 省略设置自动提交,提高性能
spring.datasource.hikari.data-source-properties.elideSetAutoCommits=true
# 不维护时间统计信息,减少性能开销
spring.datasource.hikari.data-source-properties.maintainTimeStats=false
6.3 监控配置
@Configuration
public class DataSourceMonitorConfig {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = properties.initializeDataSourceBuilder()
.type(HikariDataSource.class).build();
// 注册监控指标
new HikariDataSourceMetrics(dataSource, "app-datasource").bindTo(Metrics.globalRegistry);
return dataSource;
}
}
6.4 使用Micrometer监控连接池
- 添加依赖:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
- 配置Prometheus端点:
# 配置 Spring Boot Actuator 的 Web 端点暴露,允许暴露健康检查、应用信息和 Prometheus 指标相关端点
management.endpoints.web.exposure.include=health,info,prometheus
# 启用将应用指标数据以 Prometheus 格式导出的功能
management.metrics.export.prometheus.enabled=true
- 访问
/actuator/prometheus
可获取连接池指标数据。
7. 连接池性能测试与调优
7.1 性能测试方法
使用JMeter进行连接池性能测试:
-
测试场景:
- 模拟并发用户:50, 100, 200
- 测试时间:5分钟
- SQL操作:简单查询、复杂查询、更新操作混合
-
监控指标:
- 平均响应时间
- 吞吐量
- 错误率
- 连接池活动连接数
- 等待获取连接的线程数
7.2 调优案例
问题现象:
- 200并发时,错误率升高
- 日志中出现大量获取连接超时的异常
分析过程:
-
检查连接池配置:
spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.connection-timeout=3000
-
监控数据:
- 活动连接数经常达到20
- 等待线程数峰值达到50
解决方案:
-
增加连接池大小:
spring.datasource.hikari.maximum-pool-size=40
-
优化SQL,减少执行时间:
- 添加索引
- 优化复杂查询
-
增加获取连接超时时间:
spring.datasource.hikari.connection-timeout=5000
-
引入连接泄漏检测:
spring.datasource.hikari.leak-detection-threshold=30000
优化后结果:
- 错误率降至0.1%以下
- 平均响应时间减少30%
总结
Spring Boot数据库连接池是应用性能的关键组件之一。通过本文的详细介绍,你应该已经掌握了:
- 连接池的基本原理和核心概念
- HikariCP的详细配置和使用方法
- Druid连接池的特性和配置
- 多数据源配置的实现
- 连接池监控和性能优化技巧
- 常见问题的解决方案
- 实战案例和性能调优方法
最终建议:
- 对于大多数应用,使用默认的HikariCP即可
- 如果需要全面监控和SQL防护,考虑Druid
- 定期监控连接池状态,根据实际负载调整参数
- 遵循最佳实践,避免连接泄漏等问题
收藏的宝,2024年必脱单(我说的)!
喜欢的点个关注,想了解更多的可以关注微信公众号 “Eric的技术杂货库” ,提供更多的干货以及资料下载保存!