在 Spring Boot 中使用 Druid 连接池时,连接断开(如数据库主动关闭空闲连接、网络波动导致连接失效)是常见问题。以下从预防连接断开 、断开后自动恢复 和应用层容错三个维度提供解决方案,确保连接池的健壮性。
一、预防连接断开的核心配置
数据库连接断开的常见原因是:数据库服务器因空闲超时(如 MySQL 的 wait_timeout
)主动关闭连接,或网络波动导致连接失效。Druid 提供了连接有效性检测 和空闲连接管理机制,通过合理配置可大幅降低连接断开概率。
1. 强制校验连接有效性(关键配置)
通过 validationQuery
和 testWhileIdle
配置,确保从连接池获取的连接是有效的。
在 application.yml
中配置:
yaml
spring:
datasource:
druid:
# 开启空闲连接校验(核心!)
test-while-idle: true
# 借用连接时是否校验(生产环境建议关闭,避免额外开销)
test-on-borrow: false
# 归还连接时是否校验(生产环境建议关闭)
test-on-return: false
# 校验 SQL(轻量级查询,如 MySQL 用 SELECT 1)
validation-query: SELECT 1
# 校验超时时间(毫秒,默认 1000,建议不修改)
validation-query-timeout: 1000
- 原理 :
testWhileIdle=true
时,Druid 后台线程会定期(timeBetweenEvictionRunsMillis
)对空闲连接执行validation-query
,若失败则标记为无效并移除。 - 注意 :
validation-query
需与数据库兼容(如 Oracle 用SELECT 1 FROM DUAL
)。
2. 控制空闲连接生命周期(避免被数据库主动关闭)
数据库(如 MySQL)默认 wait_timeout=28800s
(8 小时),若连接空闲超过此时间会被关闭。Druid 需通过以下参数确保空闲连接在数据库关闭前被回收或校验:
yaml
spring:
datasource:
druid:
# 连接池后台检测线程执行间隔(默认 1 分钟,建议缩短至 10 秒)
time-between-eviction-runs-millis: 10000
# 连接在池中最小空闲时间(超过此时间会被尝试回收)
min-evictable-idle-time-millis: 300000 # 5 分钟(小于数据库 wait_timeout)
# 连接最大空闲时间(超过此时间强制回收,即使校验通过)
max-evictable-idle-time-millis: 7200000 # 2 小时(根据业务调整)
- 逻辑 :通过缩短
min-evictable-idle-time-millis
(小于数据库wait_timeout
),Druid 会在数据库关闭连接前主动回收空闲连接,避免获取到已失效的连接。
3. 避免长连接被意外关闭(可选)
对于需要长连接的业务(如批量任务),可通过 keep-alive
配置定期发送心跳维持连接(需数据库支持):
yaml
spring:
datasource:
druid:
# 开启连接保活(仅部分数据库支持,如 PostgreSQL)
keep-alive: true
# 保活 SQL(如 PostgreSQL 用 SELECT 1,MySQL 可能不生效)
keep-alive-sql: SELECT 1
# 保活间隔(毫秒,默认 30000)
keep-alive-interval-millis: 30000
二、连接断开后的自动恢复机制
即使做好预防,仍可能因网络波动或数据库重启导致连接失效。Druid 内置失效连接自动移除 和新连接自动补充机制,确保连接池快速恢复可用。
1. 自动移除失效连接
通过 remove-abandoned
配置,强制回收长时间未关闭的连接(适用于未显式关闭连接的场景):
yaml
spring:
datasource:
druid:
# 开启连接泄漏检测(超时未关闭则强制回收)
remove-abandoned: true
# 连接未关闭的超时时间(秒,默认 300 秒)
remove-abandoned-timeout: 300
# 记录泄漏连接的堆栈(调试用,生产环境可关闭)
log-abandoned: true
- 作用 :若连接被借用后超过
remove-abandoned-timeout
未关闭,Druid 会强制回收并记录日志,避免无效连接长期占用池资源。
2. 自动补充新连接
Druid 后台线程(由 time-between-eviction-runs-millis
控制)会定期检测连接池状态:
- 若活跃连接数(
activeCount
)接近maxActive
,且空闲连接数(idleCount
)不足minIdle
,会自动创建新连接补充到池中。 - 若检测到连接失效(校验失败),会直接移除并触发新连接创建。
3. 验证自动恢复效果(测试方法)
可通过以下步骤验证配置是否生效:
- 手动关闭数据库(如
mysql -e "SHUTDOWN"
)。 - 等待 10 秒(
time-between-eviction-runs-millis
),观察 Druid 监控页面(/druid/statView.html
)的activeCount
是否逐渐下降(失效连接被移除)。 - 重启数据库,观察
idleCount
是否逐渐上升(新连接被创建)。
三、应用层容错处理(补充措施)
即使连接池配置完善,仍可能偶发获取到失效连接的情况(如网络瞬断)。此时需在应用层添加容错逻辑,确保业务降级或重试。
1. 捕获 SQL 异常并重试
通过 Spring 的 @Retryable
注解(需引入 spring-retry
依赖)对数据库操作重试:
typescript
// 1. 添加依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
// 2. 配置重试策略
@Configuration
@EnableRetry
public class RetryConfig {
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate template = new RetryTemplate();
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3); // 最大重试次数
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(1000); // 重试间隔 1 秒
template.setRetryPolicy(retryPolicy);
template.setBackOffPolicy(backOffPolicy);
return template;
}
}
// 3. 在业务方法上添加重试注解
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Retryable(value = {DataAccessResourceFailureException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000))
public User getUser(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM user WHERE id = ?",
new Object[]{id},
User.class
);
}
}
- 适用场景:偶发的连接失效(如网络闪断),重试可快速恢复。
- 注意 :避免对写操作(如
INSERT/UPDATE
)无限制重试,可能导致数据重复提交。
2. 自定义连接校验(增强鲁棒性)
对于关键业务,可在获取连接后手动校验有效性(覆盖 Druid 未检测到的边缘情况):
java
@Autowired
private DataSource dataSource;
public Connection getValidConnection() throws SQLException {
Connection conn = dataSource.getConnection();
// 手动执行轻量级校验(如查询 1)
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1")) {
if (!rs.next()) {
throw new SQLException("Connection validation failed");
}
}
return conn;
}
- 注意:手动校验会增加耗时,仅建议在关键业务中使用。
四、避坑指南
- 避免
test-on-borrow=true
:每次获取连接都校验会增加数据库压力,生产环境推荐test-while-idle=true
。 - 协调数据库
wait_timeout
:若数据库wait_timeout
小于 Druid 的min-evictable-idle-time-millis
,会导致连接在池中被数据库提前关闭。需确保min-evictable-idle-time-millis < wait_timeout
(如 MySQL 默认 8 小时,Druid 设为 5 分钟)。 - 监控连接池状态 :通过
/druid/statView.html
监控activeCount
、idleCount
、destroyCount
等指标,若destroyCount
持续增加,说明存在大量失效连接,需检查validationQuery
或数据库配置。 - 版本兼容性:Druid 1.2.0+ 对连接失效处理更完善,建议使用最新稳定版(如 1.2.38)。
总结
防止连接断开的核心是通过 test-while-idle
校验 + 合理控制空闲生命周期 ,确保连接有效性;断开后的自动恢复依赖 Druid 后台线程的失效连接移除 和新连接补充 机制;应用层通过重试 或手动校验作为补充。最终通过监控(Druid 控制台、ELK)持续观察连接池状态,动态调整参数,确保系统高可用。
大家可以多多关注公众号:【泉城IT圈子】,一起学习进步