公司动态

Spring Boot多数据源架构设计与Druid监控实践

📅 2026/7/22 5:27:06
Spring Boot多数据源架构设计与Druid监控实践
1. 多数据源架构设计背景与核心挑战在复杂业务系统开发中多数据源需求主要源于以下三种典型场景业务数据隔离如多租户系统、异构数据库整合如MySQLOracle混合使用、读写分离架构实现。Spring Boot默认的单数据源自动配置机制显然无法满足这些需求而手动管理多个数据源会遇到四个关键问题连接池冲突当多个DataSource实例共用同一连接池配置时会出现连接泄漏或资源竞争事务管理混乱原生声明式事务注解Transactional无法识别具体数据源ORM框架适配MyBatis需要为每个数据源维护独立的SqlSessionFactory监控统计割裂Druid的监控数据需要按数据源维度进行区分统计以电商系统为例主库存储订单核心数据统计库存放报表数据日志库记录操作轨迹。这三个库的访问特点截然不同主库要求高事务一致性ACID统计库侧重批量查询性能日志库需要高吞吐写入2. 技术栈选型深度解析2.1 Druid连接池的优势验证相比HikariCPDruid在多数据源场景下具备三大不可替代优势监控能力通过内置StatViewServlet可实时查看各数据源的活跃连接数统计SQL执行时间分布慢查询记录Bean public ServletRegistrationBeanStatViewServlet druidStatViewServlet() { ServletRegistrationBeanStatViewServlet reg new ServletRegistrationBean(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings(/druid/*); // 添加IP白名单 reg.addInitParameter(allow, 192.168.1.100); return reg; }SQL防火墙支持配置防御规则如禁止DELETE全表操作限制WHERE条件必须包含索引字段多维度监控通过DruidStatManagerFacade可编程式获取数据源维度的连接获取次数SQL维度的执行耗时百分位2.2 MyBatisPlus的适配方案MyBatisPlus通过动态数据源插件完美支持多数据源切换其核心原理是基于AOP拦截Mapper方法调用根据DS注解值选择具体数据源通过ThreadLocal保持上下文一致性典型配置示例Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 动态数据源插件 interceptor.addInnerInterceptor(new DynamicDataSourceInnerInterceptor()); return interceptor; } }3. 完整实现流程与关键配置3.1 依赖管理规范必须严格锁定各组件版本以避免兼容性问题properties spring-boot.version2.7.3/spring-boot.version druid.version1.2.8/druid.version mybatis-plus.version3.5.2/mybatis-plus.version /properties dependencies !-- 必须排除默认连接池 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId exclusions exclusion groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId /exclusion /exclusions /dependency !-- 多数据源核心依赖 -- dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version3.5.2/version /dependency /dependencies3.2 配置文件的黄金法则application.yml中必须遵循以下结构spring: datasource: druid: # 全局公共配置 initial-size: 5 max-active: 20 validation-query: SELECT 1 dynamic: primary: master # 默认数据源 datasource: master: url: jdbc:mysql://master:3306/db?useSSLfalse driver-class-name: com.mysql.cj.jdbc.Driver druid: # 覆盖全局配置 max-active: 30 slave1: url: jdbc:mysql://slave1:3306/db?useSSLfalse driver-class-name: com.mysql.cj.jdbc.Driver关键注意事项url必须使用jdbc-url的完整格式不同数据源的连接池配置应当差异化生产环境必须配置SSL和连接超时参数3.3 事务管理的特殊处理多数据源环境下的事务需要特殊处理方案方案一XA分布式事务重型方案Bean public PlatformTransactionManager transactionManager() { return new JtaTransactionManager(); }优点强一致性保证缺点性能损耗严重方案二链式事务推荐方案Transactional(rollbackFor Exception.class) public void crossDataSourceUpdate() { orderMapper.update(order); // 操作主库 logMapper.insert(log); // 操作日志库 // 异常时两个操作都会回滚 }实现原理通过ChainedTransactionManager管理多个DataSourceTransactionManager按声明顺序依次提交/回滚4. 生产环境避坑指南4.1 连接泄漏排查三板斧现象应用运行一段时间后出现连接不足警告排查步骤检查Druid监控面板的活跃连接数SELECT * FROM druid_datasource WHERE activeCount 0抓取未关闭连接的堆栈DruidDataSource#getStackTrace()启用连接泄漏检测spring: datasource: druid: remove-abandoned: true remove-abandoned-timeout: 3004.2 MyBatisPlus分页失效问题当使用PageHelper与多数据源混用时必须配置Bean ConfigurationProperties(prefix pagehelper) public PageInterceptor pageInterceptor() { PageInterceptor interceptor new PageInterceptor(); Properties properties new Properties(); properties.setProperty(helperDialect, mysql); properties.setProperty(reasonable, true); interceptor.setProperties(properties); return interceptor; }4.3 监控数据分离方案为每个数据源配置独立的监控Bean public DruidStatInterceptor druidStatInterceptor() { DruidStatInterceptor interceptor new DruidStatInterceptor(); interceptor.setSessionStatEnable(true); interceptor.setSessionStatMaxCount(1000); return interceptor; } Bean Primary public JdbcDataSourceStatLogger jdbcDataSourceStatLogger() { return new JdbcDataSourceStatLogger(); }5. 性能调优实战参数根据压测结果推荐的配置参数参数名单数据源值多数据源值说明initialSize53初始连接数应适当降低maxActive2015总连接数各数据源maxActive之和minIdle53最小空闲连接数maxWait6000030000获取连接超时时间(ms)timeBetweenEvictionRuns6000030000检测间隔(ms)特殊场景优化批量处理任务增加maxActive和maxWait高频短查询调低validationQueryTimeout长事务场景增大removeAbandonedTimeout6. 扩展设计模式6.1 动态数据源路由通过AbstractRoutingDataSource实现运行时切换public class DynamicDataSource extends AbstractRoutingDataSource { Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.get(); } } // 使用示例 public void process() { DataSourceContextHolder.set(slave1); try { userMapper.selectById(1); } finally { DataSourceContextHolder.clear(); } }6.2 读写分离自动路由基于AOP实现自动路由Pointcut(annotation(com.xxx.annotation.ReadOnly)) public void readPointcut() {} Around(readPointcut()) public Object around(ProceedingJoinPoint pjp) { String old DataSourceContextHolder.get(); DataSourceContextHolder.set(slave); try { return pjp.proceed(); } finally { DataSourceContextHolder.set(old); } }7. 升级Spring Boot 3.x的注意事项包路径变化javax.sql - jakarta.sql需要显式引入jakarta.annotation-api配置项变更spring: datasource: druid: # 旧版 filters: stat,wall # 新版 filter: stat: enabled: true wall: enabled: true监控端点调整原/druid/* 变更为 /actuator/druid需要额外配置management.endpoints.web.exposure.includedruid8. 典型错误案例解析案例一Mapper映射失效BindingException: Invalid bound statement (not found)根本原因MyBatis扫描路径与数据源不匹配 解决方案MapperScan( basePackages com.xxx.mapper.master, sqlSessionFactoryRef masterSqlSessionFactory )案例二事务不生效Transaction not active解决方案Bean public PlatformTransactionManager transactionManager( Qualifier(masterDataSource) DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }案例三Druid监控空白解决方案Bean public FilterRegistrationBeanWebStatFilter druidWebStatFilter() { WebStatFilter filter new WebStatFilter(); FilterRegistrationBeanWebStatFilter reg new FilterRegistrationBean(); reg.setFilter(filter); reg.addUrlPatterns(/*); reg.addInitParameter(exclusions, *.js,*.css,/druid/*); return reg; }9. 单元测试规范多数据源测试的特殊要求SpringBootTest ActiveProfiles(test) class MultiDataSourceTest { Autowired private DataSource dataSource; Test void testDataSourceInjection() { assertThat(dataSource).isInstanceOf(DynamicDataSource.class); } Test Transactional Rollback void testTransaction() { // 测试跨数据源事务 } }测试资源配置示例# src/test/resources/application-test.yml spring: datasource: dynamic: datasource: master: url: jdbc:h2:mem:master;DB_CLOSE_DELAY-1 slave: url: jdbc:h2:mem:slave;DB_CLOSE_DELAY-110. 生产部署检查清单上线前必须验证的10个要点[ ] 各数据源连接池监控是否独立可见[ ] 慢SQL阈值是否按数据源分别设置[ ] 防火墙规则是否阻止了危险操作[ ] 连接泄漏检测是否开启[ ] 事务传播行为是否符合预期[ ] 分库分表策略是否冲突[ ] 连接池参数是否经过压测验证[ ] 备份数据源是否配置正确[ ] 监控指标是否接入Prometheus[ ] 故障转移机制是否经过演练