1. 动态数据源与连接池的黄金组合
在微服务架构中,多数据源访问已经成为标配需求。想象一下这样的场景:你的订单服务需要同时读写MySQL主库、查询MySQL从库、还要定期从ClickHouse拉取分析报表。如果每次操作都手动管理连接,代码很快就会变成意大利面条式的混乱结构。这就是baomidou动态数据源框架的价值所在——它让数据源切换变得像切换电视频道一样简单。
但仅有切换能力还不够,就像赛车需要仪表盘一样,我们需要实时监控连接池状态。Druid就是这个仪表盘,它能提供连接泄漏检测、SQL防火墙、性能监控等高级功能。我去年参与的一个电商项目就曾因为连接泄漏导致数据库连接耗尽,引入Druid后我们通过它的监控界面快速定位到了问题代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与依赖配置
2.1 依赖管理避坑指南
创建新的Spring Boot项目时,建议使用Spring Initializr生成基础骨架。这里有个容易踩的坑:baomidou的dynamic-datasource-spring-boot-starter已经内置了HikariCP连接池,如果同时引入Druid的starter会导致冲突。正确的做法是排除HikariCP:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
<exclusions>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
实测发现,版本匹配很关键。有次升级Spring Boot到2.7.x后,由于Druid版本滞后导致监控页面无法打开。建议保持版本对应关系:
- Spring Boot 2.4.x → Druid 1.2.8
- Spring Boot 2.7.x → Druid 1.2.16
2.2 基础配置模板
在application.yml中,我们需要配置三层结构:动态数据源路由、各数据源连接信息、Druid专属参数。这是我经过多个项目验证的模板配置:
yaml复制spring:
datasource:
dynamic:
primary: master
strict: true
datasource:
master:
url: jdbc:mysql://localhost:3306/main_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 5
max-active: 20
filters: stat,wall
slave1:
url: jdbc:mysql://replica1:3306/read_db
username: reader
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
test-while-idle: true
validation-query: SELECT 1
注意strict参数建议生产环境设为true,这样在切换不存在的DS时会立即报错,避免静默失败。开发阶段可以设为false方便调试。
3. 高级配置与性能调优
3.1 Druid监控中心配置
Druid最强大的功能莫过于它的监控能力。要启用监控面板,需要添加以下配置:
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", "admin123");
return reg;
}
@Bean
public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
FilterRegistrationBean<WebStatFilter> reg = new FilterRegistrationBean<>();
reg.setFilter(new WebStatFilter());
reg.addUrlPatterns("/*");
reg.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return reg;
}
}
启动应用后访问/druid路径,你会看到一个功能丰富的监控面板。我特别喜欢它的SQL监控功能,能清晰看到慢SQL排行和执行时间分布。曾经通过这个功能发现某个报表查询没有使用索引,优化后响应时间从3秒降到了200毫秒。
3.2 连接池参数调优
Druid提供了数十个可调参数,但实际项目中只需要关注几个关键指标:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| initialSize | 5-10 | 初始化连接数,根据并发量调整 |
| maxActive | 20-100 | 最大连接数,超过会导致等待 |
| minIdle | 5-10 | 最小空闲连接,避免频繁创建 |
| maxWait | 60000 | 获取连接超时时间(ms) |
| timeBetweenEvictionRunsMillis | 60000 | 检测间隔(ms) |
对于读写分离场景,建议为主库设置更大的maxActive值,因为写操作通常更耗时。从库可以适当减小,比如:
- 主库:maxActive=50
- 从库:maxActive=30
4. 实战中的动态切换技巧
4.1 注解式切换的最佳实践
baomidou提供的@DS注解非常灵活,但使用不当会导致意外行为。这是我的经验总结:
- 类级注解:适用于整个Mapper使用同一数据源的情况
java复制@Mapper
@DS("slave1")
public interface ReportMapper {
List<Report> getDailyReport();
}
- 方法级注解:优先级高于类级注解
java复制@Mapper
@DS("master")
public interface OrderMapper {
@DS("slave1")
List<Order> queryOrders(OrderQuery query);
@DS("master")
void createOrder(Order order);
}
- 事务中的陷阱:在@Transactional方法内切换数据源无效,因为事务已经绑定了初始数据源。解决方案是使用编程式事务管理。
4.2 编程式切换方案
对于需要根据运行时条件动态切换的场景,可以使用DynamicDataSourceContextHolder:
java复制public List<Data> fetchData(boolean useBackup) {
try {
if(useBackup) {
DynamicDataSourceContextHolder.push("backup");
}
return dataMapper.selectList();
} finally {
if(useBackup) {
DynamicDataSourceContextHolder.clear();
}
}
}
在灰度发布场景中,我曾用这种方式实现新老数据库的流量切换:当新库出现问题时,自动回切到旧库,保证服务可用性。
5. 生产环境注意事项
5.1 多数据源事务管理
跨数据源的事务需要引入JTA解决方案,比如Atomikos。配置示例:
java复制@Bean
public PlatformTransactionManager transactionManager(DynamicDataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public JtaTransactionManager jtaTransactionManager() {
return new JtaTransactionManager();
}
但分布式事务性能开销大,实际项目中更推荐使用最终一致性方案,比如:
- 本地消息表
- 事务消息
- Saga模式
5.2 监控指标集成
除了Druid自带的监控,建议将关键指标导出到Prometheus:
yaml复制spring:
datasource:
druid:
stat-view-servlet:
enabled: true
web-stat-filter:
enabled: true
filter:
stat:
merge-sql: true
log-slow-sql: true
slow-sql-millis: 1000
配合Grafana可以打造更强大的监控看板,设置合理的告警阈值,比如:
- 活跃连接数超过maxActive的80%
- SQL执行时间超过1秒
- 连接等待时间超过500ms
6. 典型问题排查指南
6.1 连接泄漏排查
当发现连接数持续增长不释放时,可以:
- 访问Druid的/weburi.html查看URI统计
- 检查最频繁调用的SQL
- 在代码中搜索对应的Mapper方法
- 确认是否有未关闭的ResultSet或Connection
我曾遇到一个案例:分页查询没有设置合理的limit,导致内存中加载了百万级数据,连接无法释放。通过Druid的SQL监控快速定位到了问题SQL。
6.2 性能瓶颈分析
对于响应变慢的问题,Druid的监控面板特别有用:
- 查看"SQL监控"页面的执行时间排行
- 检查"URI监控"中的慢请求
- 分析"Spring监控"中的方法调用链
有个实际案例:某个统计接口突然变慢,通过分析发现是缺少复合索引导致。添加索引后性能提升了20倍。
