连接池调优
数据库连接池是 MyBatis 与数据库之间的桥梁。合理配置连接池参数可显著提升并发处理能力,避免连接耗尽、线程阻塞、资源浪费等问题。本文对比 HikariCP 与 Druid 的核心参数,给出生产环境的调优方案。
为什么需要连接池
每次创建数据库连接涉及:
- TCP 三次握手(网络延迟)
- 数据库认证(CPU 计算)
- 分配连接资源(内存)
单次建连耗时约 20ms ~ 50ms。连接池通过预创建、复用连接,将获取连接耗时降低至亚毫秒级。
HikariCP 连接池调优
HikariCP 是 Spring Boot 2.0+ 的默认连接池,以高性能、低延迟著称。
核心参数配置
spring:
datasource:
hikari:
# 连接池最大连接数
maximum-pool-size: 20
# 最小空闲连接数
minimum-idle: 5
# 连接最大存活时间(毫秒),默认 30 分钟
max-lifetime: 1800000
# 连接超时时间(毫秒),默认 30 秒
connection-timeout: 30000
# 空闲连接超时时间(毫秒),默认 10 分钟
idle-timeout: 600000
# 连接测试查询
connection-test-query: SELECT 1
# 数据源名称(便于监控识别)
pool-name: MyHikariPool
核心参数详解
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
maximum-pool-size | 最大连接数 | CPU 核数 × 2 + 磁盘数 | 并非越大越好,过多连接反而降低吞吐 |
minimum-idle | 最小空闲连接 | 与 maximum-pool-size 相同或略低 | Hikari 官方建议不设置,让池自动管理 |
max-lifetime | 连接最大存活时间 | 比数据库超时少 30 秒 | MySQL 默认 wait_timeout=28800(8小时),建议 25 ~ 30 分钟 |
connection-timeout | 获取连接超时 | 30,000(30秒) | 超过此时间抛出 SQLTransientConnectionException |
idle-timeout | 空闲连接回收超时 | 600,000(10分钟) | 仅当 minimum-idle < maximum-pool-size 时生效 |
注意:HikariCP 官方作者建议
minimum-idle不要设置,保持与maximum-pool-size相等,让连接池维持固定大小,避免频繁扩缩带来的性能抖动。
maximum-pool-size 计算公式
最佳连接数 = CPU 核数 × 2 + 有效磁盘数
经验推导:
- CPU 密集型操作(大量计算、复杂 SQL):连接数 ≈ CPU 核数
- IO 密集型操作(简单查询、批量写入):连接数 ≈ CPU 核数 × 2 ~ 4
- 混合场景:连接数 ≈ CPU 核数 × 2 + 磁盘数
| CPU 核数 | IO 密集型推荐 | CPU 密集型推荐 |
|---|---|---|
| 4 | 8 ~ 12 | 4 ~ 6 |
| 8 | 16 ~ 24 | 8 ~ 12 |
| 16 | 32 ~ 48 | 16 ~ 24 |
注意:连接数超过数据库服务器的处理能力时,多余连接只会增加线程竞争和上下文切换开销。生产环境应结合压测数据确定最终值。
Java Config 方式
@Configuration
public class DataSourceConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(20);
config.setMinimumIdle(10);
config.setMaxLifetime(1800000);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setConnectionTestQuery("SELECT 1");
config.setPoolName("MyHikariPool");
// 连接池监控
config.setMetricRegistry(metricRegistry());
return new HikariDataSource(config);
}
}
Druid 连接池调优
Druid 是阿里巴巴开源的连接池,自带监控统计和 SQL 分析功能,在国内使用广泛。
核心参数配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 初始连接数
initial-size: 5
# 最大活跃连接数
max-active: 20
# 最小空闲连接数
min-idle: 5
# 获取连接最大等待时间(毫秒)
max-wait: 60000
# 连接超时时间(毫秒)
connect-timeout: 30000
# Socket 超时(毫秒)
socket-timeout: 60000
# 空闲连接检测间隔(毫秒)
time-between-eviction-runs-millis: 60000
# 连接在池中最小生存时间(毫秒)
min-evictable-idle-time-millis: 300000
# 连接在池中最大生存时间(毫秒)
max-evictable-idle-time-millis: 900000
# 检测连接是否有效的 SQL
validation-query: SELECT 1
# 申请连接时执行 validation-query
test-while-idle: true
test-on-borrow: false
test-on-return: false
核心参数对比
| 参数 | HikariCP | Druid | 说明 |
|---|---|---|---|
| 最大连接数 | maximum-pool-size | max-active | 推荐值一致 |
| 最小空闲 | minimum-idle | min-idle | Hikari 建议不设置,Druid 可设置 |
| 获取超时 | connection-timeout | max-wait | 建议 30 秒 |
| 连接存活时间 | max-lifetime | max-evictable-idle-time-millis | 建议 25 ~ 30 分钟 |
| 空闲超时 | idle-timeout | min-evictable-idle-time-millis | 建议 10 分钟 |
| 有效性检测 | connection-test-query | validation-query | Hikari 默认自动检测,Druid 需手动指定 |
Druid 监控配置
Druid 的优势之一是内置监控页面和 SQL 分析:
@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServlet() {
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>();
bean.setServlet(new StatViewServlet());
bean.addUrlMappings("/druid/*");
bean.addInitParameter("loginUsername", "admin");
bean.addInitParameter("loginPassword", "admin123");
bean.addInitParameter("resetEnable", "false");
return bean;
}
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter() {
FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new WebStatFilter());
bean.addUrlPatterns("/*");
bean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*");
return bean;
}
}
Druid SQL 防火墙
spring:
datasource:
druid:
filter:
stat:
slow-sql-millis: 2000 # 超过 2 秒记录为慢 SQL
log-slow-sql: true # 打印慢 SQL 日志
wall:
enabled: true # 开启 SQL 防火墙
config:
delete-allow: false # 禁止 DELETE 无 WHERE 条件
drop-table-allow: false # 禁止 DROP TABLE
连接泄漏检测
HikariCP 泄漏检测
spring:
datasource:
hikari:
leak-detection-threshold: 60000 # 超过 60 秒未归还连接则告警
WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detected:
Connection leased for 60,012ms. Threshold: 60,000ms
at com.example.service.UserService.getUser(UserService.java:42)
注意:
leak-detection-threshold仅用于开发/测试环境,生产环境不建议开启,因为它会对未超时连接也产生额外开销。
Druid 泄漏检测
spring:
datasource:
druid:
remove-abandoned: true # 开启泄漏检测
remove-abandoned-timeout: 180 # 超过 180 秒未归还则回收
log-abandoned: true # 打印泄漏堆栈
常见连接池问题排查
连接耗尽
现象:应用响应缓慢,日志出现 Connection is not available, request timed out
排查步骤:
- 检查
maximum-pool-size是否过小 - 检查是否存在未关闭的 SqlSession / Connection
- 检查是否存在慢查询长时间占用连接
- 监控连接池活跃连接数与等待线程数
// 正确:使用 try-with-resources 确保 Session 关闭
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
return mapper.selectById(id);
}
// session 自动关闭,连接自动归还到池
连接频繁重建
现象:连接池日志频繁出现 Connection created / Connection closed
原因:max-lifetime 设置过短,或数据库侧 wait_timeout 小于连接池配置,导致连接被数据库强制关闭。
解决:确保 max-lifetime 比数据库 wait_timeout 少至少 30 秒。
-- 查看 MySQL wait_timeout
SHOW VARIABLES LIKE 'wait_timeout';
-- 输出:28800(8小时)
-- 推荐连接池 max-lifetime < wait_timeout - 30秒
-- 建议:25 ~ 30 分钟即可,无需匹配数据库 8 小时
空闲连接被防火墙回收
现象:连接池中的连接长时间不使用,被中间防火墙/NAT 设备强制断开,使用时报错。
解决:设置合理的 idle-timeout 和心跳检测:
spring:
datasource:
hikari:
idle-timeout: 300000 # 5 分钟空闲即回收
keepalive-time: 30000 # 每 30 秒发送心跳保活
max-lifetime: 1800000 # 30 分钟强制重建
MyBatis 与连接池集成
MyBatis 原生配置(不推荐,Spring 环境下由 Spring 管理)
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
<property name="poolMaximumActiveConnections" value="20"/>
<property name="poolMaximumIdleConnections" value="5"/>
<property name="poolMaximumCheckoutTime" value="20000"/>
<property name="poolTimeToWait" value="20000"/>
</dataSource>
</environment>
</environments>
Spring Boot 集成(推荐)
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
@Autowired
private DataSource dataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
// MyBatis 自身配置
org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
config.setMapUnderscoreToCamelCase(true);
config.setCacheEnabled(true);
config.setLazyLoadingEnabled(true);
factoryBean.setConfiguration(config);
return factoryBean.getObject();
}
}
要点总结
- HikariCP 性能优于 Druid,是 Spring Boot 默认连接池;Druid 优势在于监控和 SQL 分析
maximum-pool-size推荐值 = CPU 核数 × 2 + 磁盘数,并非越大越好max-lifetime应比数据库wait_timeout少 30 秒以上,推荐 25 ~ 30 分钟- HikariCP 官方建议不设置
minimum-idle,保持固定池大小 - 开启泄漏检测用于开发环境排查,生产环境不建议开启
- 正确关闭 SqlSession 避免连接泄漏,推荐使用 try-with-resources
- 防火墙/NAT 环境下设置
keepalive-time保活连接 - Druid 可配置 SQL 防火墙和慢 SQL 日志,适合安全审计场景
📝 发现内容有误?点击此处直接编辑