1. 为什么需要Druid连接池
在Spring Boot应用开发中,数据库连接管理是个容易被忽视但极其重要的问题。我见过太多生产环境事故都源于连接池配置不当——连接泄漏导致系统崩溃、突发流量时连接数不足造成请求堆积、缺乏监控导致问题无法及时发现。这些正是Druid要解决的核心痛点。
Druid作为阿里巴巴开源的数据库连接池,相比HikariCP等方案有几个独特优势:
- 监控能力:内置的StatFilter能提供SQL执行统计、连接池状态等关键指标
- 防御性设计:支持连接泄漏检测、SQL注入防护等企业级特性
- 可扩展性:通过Filter机制可以方便地扩展功能
- 国产化适配:对国内数据库(如达梦、OceanBase)有更好的兼容性
提示:虽然HikariCP在基准测试中性能略优,但Druid的综合运维能力使其成为复杂业务场景的更优选择
2. Spring Boot 3集成Druid全流程
2.1 基础依赖配置
首先在pom.xml中添加最新依赖(截至2024年1月):
xml复制<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.18</version>
</dependency>
注意Spring Boot 3必须使用druid-spring-boot-3-starter而非旧版starter,否则会引发兼容性问题。我曾在这个问题上浪费了两小时排查时间。
2.2 基础配置参数
在application.yml中配置核心参数:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
关键参数说明:
| 参数 | 推荐值 | 作用说明 |
|---|---|---|
| initial-size | 5-10 | 初始化连接数,避免首次请求等待 |
| max-active | 20-100 | 根据服务器CPU核心数和业务压力调整 |
| max-wait | 1000-60000 | 获取连接超时时间(ms),太长会堆积请求 |
| time-between-eviction-runs-millis | 60000 | 连接检测线程运行间隔 |
2.3 监控面板配置
启用内置监控需要添加Servlet配置:
java复制@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean<StatViewServlet> druidServlet() {
ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<>();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
// 配置监控页面的登录账号
reg.addInitParameter("loginUsername", "admin");
reg.addInitParameter("loginPassword", "druid123");
// 允许哪些IP访问
reg.addInitParameter("allow", "192.168.1.100,127.0.0.1");
return reg;
}
}
访问http://localhost:8080/druid即可看到监控页面,这里可以:
- 查看SQL执行统计
- 监控连接池状态
- 分析慢SQL
- 查看URI访问统计
警告:生产环境务必修改默认账号密码并限制访问IP,我曾遇到扫描器暴力破解Druid控制台的案例
3. 高级配置与优化技巧
3.1 SQL防火墙配置
在application.yml中启用防御性功能:
yaml复制spring:
datasource:
druid:
filters: stat,wall,slf4j
wall:
config:
delete-allow: false
drop-table-allow: false
none-base-statement-allow: false
这样配置后:
- 禁止执行DELETE语句(根据业务需要调整)
- 禁止DROP TABLE等危险操作
- 拦截永真条件(如WHERE 1=1)的SQL注入尝试
3.2 连接泄漏检测
开发阶段可以开启连接泄漏检测:
yaml复制spring:
datasource:
druid:
remove-abandoned: true
remove-abandoned-timeout: 180
log-abandoned: true
当连接超过180秒未关闭会被强制回收,并在日志中打印警告。这个功能帮我在代码评审前就发现了多处未关闭的连接。
3.3 多数据源配置
实际项目常需要连接多个数据库:
java复制@Configuration
public class MultiDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@Primary
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}
}
对应yml配置:
yaml复制spring:
datasource:
druid:
master:
url: jdbc:mysql://master:3306/db
username: root
password: master123
slave:
url: jdbc:mysql://slave:3306/db
username: root
password: slave123
4. 生产环境最佳实践
4.1 监控指标集成
将Druid监控数据接入Prometheus:
java复制@Bean
public DruidStatMetrics druidStatMetrics(DataSource dataSource) {
return new DruidStatMetrics(dataSource);
}
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("application", "your-app-name");
}
然后在Prometheus配置中添加抓取目标:
yaml复制scrape_configs:
- job_name: 'spring'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['localhost:8080']
4.2 动态配置调整
运行时动态修改连接池参数:
java复制@RestController
@RequestMapping("/druid")
public class DruidAdminController {
@Autowired
private DataSource dataSource;
@PostMapping("/config")
public String updateConfig(@RequestParam int maxActive) {
DruidDataSource druidDataSource = (DruidDataSource) dataSource;
druidDataSource.setMaxActive(maxActive);
return "Max active connections updated to: " + maxActive;
}
}
4.3 常见问题排查
问题1:连接池达到max-active后请求卡住
- 检查是否有连接泄漏(未关闭的连接)
- 适当增大max-wait时间
- 分析监控中的SQL执行时间是否过长
问题2:监控页面无法访问
- 检查是否添加了@ServletComponentScan注解
- 确认没有安全框架(如Spring Security)拦截了/druid/*路径
问题3:启动时报兼容性错误
- Spring Boot 3必须使用druid-spring-boot-3-starter
- JDK 17+需要Druid 1.2.8以上版本
5. 性能调优实战
5.1 连接池大小计算
根据PostgreSQL官方建议,最优连接数公式为:
code复制连接数 = ((核心数 * 2) + 有效磁盘数)
对于4核CPU+SSD的服务器:
- 计算连接数 = (4 * 2) + 1 = 9
- 建议配置:
yaml复制initial-size: 5 min-idle: 5 max-active: 20 # 留出突发流量余量
5.2 连接验证优化
默认的validation-query: SELECT 1在某些数据库上性能不佳,可以改用:
yaml复制# MySQL
validation-query: SELECT 1 FROM DUAL
# PostgreSQL
validation-query: SELECT 1
# Oracle
validation-query: SELECT 1 FROM DUAL
5.3 监控过滤器配置
精确统计慢SQL和异常SQL:
yaml复制spring:
datasource:
druid:
filters: stat,wall
filter:
stat:
slow-sql-millis: 1000
log-slow-sql: true
merge-sql: true
wall:
log-violation: true
throw-exception: false
这个配置会:
- 记录执行超过1秒的SQL
- 合并相同SQL的统计
- 记录但不阻断违规SQL
6. 安全加固方案
6.1 监控页面防护
生产环境必须加强安全措施:
java复制@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter() {
WebStatFilter filter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> reg = new FilterRegistrationBean<>(filter);
reg.addUrlPatterns("/*");
reg.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*");
// 添加IP白名单
reg.addInitParameter("principalSessionName", "SESSION_USER");
reg.addInitParameter("principalCookieName", "USER_COOKIE");
return reg;
}
6.2 敏感信息加密
避免数据库密码明文存储:
- 使用Druid的ConfigFilter加密:
shell复制java -cp druid-1.2.18.jar com.alibaba.druid.filter.config.ConfigTools your_password
- 在配置中使用加密后的值:
yaml复制spring:
datasource:
password: MFyH1eDnU0GzH6Tj7Q8JjW9LkK5jH4Q2
druid:
filter:
config:
enabled: true
6.3 审计日志配置
记录所有SQL操作到单独日志文件:
yaml复制logging:
level:
com.alibaba.druid.filter.logging.Log4jFilter: DEBUG
file:
name: logs/sql-audit.log
path: /var/log
在logback-spring.xml中配置审计日志策略:
xml复制<appender name="SQL_AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/sql-audit.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
