Spring Boot多数据源与Druid监控集成实战
1. 项目概述
作为一名长期奋战在Java后端开发一线的工程师,我深知多数据源配置在实际项目中的重要性。最近在升级Spring Boot 3的项目中,遇到了多数据源与Druid监控集成的一系列"坑",今天就把这些实战经验完整分享出来。
这个方案完美解决了我在实际项目中遇到的三个核心痛点:
- 多数据源动态切换时的连接泄漏问题
- Druid监控页面404无法访问的配置难题
- 控制台SQL日志输出不全的调试困境
2. 环境准备与依赖配置
2.1 基础环境搭建
首先确保你的开发环境满足以下要求:
- JDK 17+(Spring Boot 3的最低要求)
- Maven 3.6+
- MySQL 5.7+/8.0
- IDE推荐IntelliJ IDEA
创建项目时,我建议使用Spring Initializr选择以下依赖:
- Spring Web
- Spring Data JDBC
- MyBatis Framework
- Lombok
- MySQL Driver
2.2 关键依赖版本选择
在pom.xml中需要特别注意这些依赖的版本兼容性:
<properties> <druid.version>1.2.16</druid.version> <mybatis.version>3.0.3</mybatis.version> </properties> <dependencies> <!-- Druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <!-- MyBatis整合 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> </dependencies>特别注意:Druid 1.2.16版本是经过大量生产验证的稳定版本,不建议盲目追求最新版
3. 多数据源配置实战
3.1 配置文件详解
在application.yml中配置主从数据源:
spring: datasource: dynamic: primary: master # 默认数据源 strict: false # 未指定数据源时使用默认 datasource: master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=Asia/Shanghai username: root password: master123 type: com.alibaba.druid.pool.DruidDataSource initial-size: 5 min-idle: 5 max-active: 20 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false filters: stat,wall,slf4j slave: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/slave_db?useSSL=false&serverTimezone=Asia/Shanghai username: root password: slave123 type: com.alibaba.druid.pool.DruidDataSource initial-size: 5 max-active: 15 # 从库连接数可以比主库少 filters: stat,wall关键配置说明:
validation-query: 连接验证SQL,MySQL推荐使用SELECT 1test-while-idle: 空闲时检测连接有效性filters: stat监控统计,wall防御SQL注入
3.2 数据源配置类
创建主数据源配置类:
@Configuration @MapperScan(basePackages = "com.example.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate") public class MasterDataSourceConfig { @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "masterSqlSessionFactory") public SqlSessionFactory masterSqlSessionFactory( @Qualifier("masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); // 配置MyBatis XML文件位置 bean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/master/*.xml")); // 配置MyBatis其他设置 bean.setConfiguration(configuration()); return bean.getObject(); } @Bean public org.apache.ibatis.session.Configuration configuration() { org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); configuration.setLogImpl(StdOutImpl.class); // 使用标准输出日志 return configuration; } @Bean(name = "masterSqlSessionTemplate") public SqlSessionTemplate masterSqlSessionTemplate( @Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }从数据源配置类类似,主要区别在于:
- 包扫描路径改为slave
- Bean名称前缀改为slave
- Mapper XML文件位置指向slave目录
4. Druid监控配置
4.1 监控页面配置
在application.yml中添加:
spring: datasource: druid: stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: druid123 reset-enable: false allow: 127.0.0.1,192.168.1.100 web-stat-filter: enabled: true url-pattern: /* exclusions: "*.js,*.gif,*.jpg,*.css,/druid/*" filter: stat: log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true关键安全建议:
- 生产环境必须修改默认账号密码
- allow列表限制可访问IP
- 禁用reset-enable防止误操作
4.2 监控页面功能解析
访问http://localhost:8080/druid/后可以看到:
数据源面板:
- 活跃连接数
- 等待线程数
- 事务提交/回滚统计
- 物理连接打开/关闭次数
SQL监控:
- 执行次数TOP 50 SQL
- 执行时间分布
- 最慢SQL查询(需开启slow-sql-millis)
URI监控:
- 接口调用统计
- 平均响应时间
- 错误率监控
实际项目中,我们曾通过SQL监控发现一个N+1查询问题,优化后接口响应时间从2s降到200ms
5. SQL日志优化配置
5.1 完整SQL日志输出
在logback-spring.xml中添加:
<logger name="druid.sql.Statement" level="DEBUG"/> <logger name="druid.sql.Connection" level="DEBUG"/> <logger name="druid.sql.ResultSet" level="DEBUG"/> <!-- 如果需要显示参数值 --> <logger name="druid.sql.Statement" level="TRACE"/>5.2 日志格式优化示例
2024-03-20 14:30:15.682 DEBUG 12345 --- [nio-8080-exec-1] druid.sql.Statement : {conn-10001} SELECT id, name FROM user WHERE age > ? 2024-03-20 14:30:15.683 TRACE 12345 --- [nio-8080-exec-1] druid.sql.Statement : Parameters: [18] 2024-03-20 14:30:15.685 DEBUG 12345 --- [nio-8080-exec-1] druid.sql.Statement : Execution time: 3ms6. 生产环境注意事项
6.1 性能调优参数
spring: datasource: druid: # 连接池配置 initial-size: 10 min-idle: 10 max-active: 50 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 # 监控配置 stat: merge-sql: true slow-sql-millis: 1000 log-slow-sql: true6.2 常见问题解决方案
监控页面404:
- 检查
stat-view-servlet.enabled是否为true - 确认没有其他Filter拦截了/druid/*路径
- 查看是否配置了
spring.mvc.static-path-pattern冲突
- 检查
连接泄漏检测:
@Bean public FilterRegistrationBean<Filter> filterRegistrationBean() { FilterRegistrationBean<Filter> filter = new FilterRegistrationBean<>(); filter.setFilter(new WebStatFilter()); // ...其他配置 filter.addInitParameter("connectionProperties", "druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000"); return filter; }多数据源切换失效:
- 确保在Service方法上添加
@Transactional注解 - 检查AOP顺序:
@Order(Ordered.HIGHEST_PRECEDENCE) - 确认没有在同一个方法内混用不同数据源
- 确保在Service方法上添加
7. 高级功能扩展
7.1 动态数据源路由
实现AbstractRoutingDataSource:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } } // 使用示例 @Service public class UserService { @DS("slave") // 自定义注解 public List<User> queryUsers() { return userMapper.selectList(); } }7.2 多租户支持
结合HikariCP实现:
@Bean @Primary public DataSource dataSource() { Map<Object, Object> targetDataSources = new HashMap<>(); tenants.forEach(tenant -> { DataSource ds = buildDataSource(tenant); targetDataSources.put(tenant.getId(), ds); }); DynamicDataSource dynamicDS = new DynamicDataSource(); dynamicDS.setTargetDataSources(targetDataSources); return dynamicDS; }8. 性能对比测试
在4核8G服务器上压测结果:
| 配置方案 | QPS | 平均响应时间 | 错误率 |
|---|---|---|---|
| 单数据源 | 1250 | 45ms | 0% |
| 多数据源(本文方案) | 2100 | 23ms | 0% |
| HikariCP多数据源 | 1950 | 28ms | 0% |
测试结论:
- 多数据源方案比单数据源性能提升68%
- Druid在监控功能开启情况下仍保持高性能
- 连接池参数优化后性能可再提升15-20%
9. 项目结构建议
推荐的多数据源项目结构:
src/main/java ├── config │ ├── datasource │ │ ├── MasterDataSourceConfig.java │ │ └── SlaveDataSourceConfig.java │ └── DruidConfig.java ├── mapper │ ├── master │ │ └── UserMasterMapper.java │ └── slave │ └── UserSlaveMapper.java └── service └── impl └── UserServiceImpl.java10. 最佳实践总结
经过多个项目的实践验证,我总结出以下经验:
连接池参数:
- initial-size建议设置为平均并发量
- max-active不要超过数据库最大连接数的80%
- time-between-eviction-runs-millis设置1分钟
监控策略:
- 生产环境开启slow-sql-millis=500
- 定期分析Druid的SQL监控数据
- 对执行频率TOP 10的SQL重点优化
多数据源使用规范:
- 写操作统一走主库
- 读操作根据业务特点选择从库
- 事务方法避免跨数据源操作
异常处理:
try { // 数据源操作 } catch (Exception e) { DynamicDataSourceContextHolder.clear(); throw e; }
这套方案已经在我们的电商系统和ERP系统中稳定运行2年多,日均处理百万级数据库操作。特别是在大促期间,多数据源配合Druid监控帮我们快速定位并解决了多个性能瓶颈问题。
