当前位置: 首页 > news >正文

SpringBoot项目里,用Dynamic-Datasource和Druid搞定多数据库读写(附完整配置)

SpringBoot多数据源实战:Dynamic-Datasource与Druid的高阶组合方案

当你的订单服务需要同时写入MySQL交易库和MongoDB日志库时,当报表系统要混合查询Oracle数仓和ClickHouse实时表时,多数据源架构就成为刚需。但原生SpringBoot的单一数据源支持显然力不从心。本文将带你用Dynamic-Datasource+Druid这对黄金组合,实现企业级多数据源管控。

1. 为什么选择Dynamic-Datasource与Druid组合

在电商大促期间,我们曾遇到一个典型场景:订单库QPS突破5000后,默认连接池出现连接泄漏,而Druid的监控界面立即定位到未关闭的连接。这正是我们推荐组合方案的核心原因:

  • Dynamic-Datasource的灵活切换:基于@DS注解的声明式路由,支持方法级细粒度控制
  • Druid的工业级特性
    • SQL防注入防火墙
    • 可视化监控统计(连接池状态、慢SQL、执行频次)
    • 内置连接泄漏检测
    • 支持分库分表场景下的合并统计

对比测试显示,在100并发下,Druid比HikariCP减少30%的上下文切换开销。以下是主流连接池关键指标对比:

特性DruidHikariCPTomcat JDBC
监控界面✅ 完整❌ 无❌ 无
防注入✅ 支持❌ 不支持❌ 不支持
连接泄漏检测✅ 毫秒级❌ 无⚠️ 秒级
最大连接数稳定性✅ 优秀⚠️ 偶现波动❌ 较差

2. 环境准备与依赖配置

2.1 关键依赖排除策略

首先创建SpringBoot 2.7.x项目,在pom.xml中需要特别注意依赖冲突问题:

<dependencies> <!-- 核心依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.6.1</version> <!-- 排除默认连接池 --> <exclusions> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency> <!-- Druid starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.16</version> </dependency> <!-- 必须排除原生自动配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <exclusions> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency> </dependencies>

提示:如果出现"Failed to configure a DataSource"错误,需要在启动类添加:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DruidDataSourceAutoConfigure.class })

2.2 多环境配置模板

在application.yml中配置主从数据源+异构数据库示例:

spring: datasource: dynamic: primary: master strict: true # 生产环境建议开启严格模式 datasource: master: url: jdbc:mysql://master-db:3306/order?useSSL=false username: admin password: ${{DB_PASSWORD}} driver-class-name: com.mysql.cj.jdbc.Driver druid: initial-size: 5 max-active: 50 validation-query: SELECT 1 test-while-idle: true filters: stat,wall slave: url: jdbc:mysql://slave-db:3306/order?useSSL=false username: readonly password: ${{DB_PASSWORD}} driver-class-name: com.mysql.cj.jdbc.Driver druid: max-active: 30 validation-query: SELECT 1 mongodb_log: url: mongodb://log-db:27017/operation_log druid: max-active: 20

3. 高级配置与性能调优

3.1 Druid监控中心配置

在SpringBoot中激活Druid监控界面:

@Configuration public class DruidConfig { @Bean public ServletRegistrationBean<StatViewServlet> druidServlet() { ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<>(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); // 白名单配置 reg.addInitParameter("allow", "192.168.1.100"); // 监控页面登录凭证 reg.addInitParameter("loginUsername", "admin"); reg.addInitParameter("loginPassword", "druid@123"); return reg; } @Bean public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() { FilterRegistrationBean<WebStatFilter> reg = new FilterRegistrationBean<>(); reg.setFilter(new WebStatFilter()); reg.addUrlPatterns("/*"); reg.addInitParameter("exclusions", "*.js,*.css,/druid/*"); return reg; } }

访问http://localhost:8080/druid即可查看:

  • 实时数据源状态
  • SQL执行排行榜
  • 慢SQL监控
  • URI访问统计

3.2 多数据源事务处理

在跨数据源操作时,需要特别处理分布式事务。推荐使用Seata的AT模式:

@Service public class OrderService { @DS("master") @Transactional public void createOrder(Order order) { orderMapper.insert(order); // 跨数据源操作需要单独开启事务 TransactionTemplate template = new TransactionTemplate(transactionManager); template.execute(status -> { logService.recordOperationLog(buildLog(order)); return Boolean.TRUE; }); } @DS("mongodb_log") @Transactional(propagation = Propagation.REQUIRES_NEW) public void recordOperationLog(Log log) { logMapper.insert(log); } }

4. 实战:电商订单多库操作

4.1 读写分离场景实现

@RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderService orderService; // 读操作自动路由到slave @DS("slave") @GetMapping("/{id}") public Order getOrder(@PathVariable Long id) { return orderService.getById(id); } // 写操作使用master数据源 @DS("master") @PostMapping public void createOrder(@RequestBody Order order) { orderService.save(order); } }

4.2 动态数据源路由进阶

实现基于请求参数的动态路由:

public class TenantDataSourceSelector extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { String tenantId = RequestContextHolder.getRequestAttributes() .getAttribute("tenant", RequestAttributes.SCOPE_REQUEST); return StringUtils.isNotBlank(tenantId) ? tenantId : "master"; } } // 在配置类中注册 @Bean @Primary public DataSource dataSource() { DynamicRoutingDataSource ds = new DynamicRoutingDataSource(); ds.setPrimary("master"); ds.setStrategy("com.example.TenantDataSourceSelector"); return ds; }

5. 生产环境注意事项

  1. 连接泄漏检测:建议开启Druid的removeAbandoned配置

    druid: remove-abandoned: true remove-abandoned-timeout: 300 log-abandoned: true
  2. 监控指标采集:将Druid统计信息接入Prometheus

    @Bean public DruidStatViewServlet druidStatViewServlet() { DruidStatViewServlet servlet = new DruidStatViewServlet(); servlet.setUrlPatterns(Arrays.asList("/druid/*")); servlet.setResetEnable(false); return servlet; }
  3. 多数据源健康检查:自定义HealthIndicator

    @Component public class MultiDataSourceHealthIndicator implements HealthIndicator { @Autowired private DynamicDataSourceProperties properties; @Override public Health health() { Map<String, DataSource> dataSources = DynamicDataSourceContextHolder.getDataSources(); Health.Builder builder = Health.up(); dataSources.forEach((name, ds) -> { try (Connection conn = ds.getConnection()) { builder.withDetail(name, "UP"); } catch (Exception e) { builder.withDetail(name, "DOWN - " + e.getMessage()); } }); return builder.build(); } }

在最近的一次金融级项目中,这套组合方案成功支撑了日均3亿级的跨库交易。特别提醒:在K8s环境中,需要适当调低max-active值以避免Pod内存溢出。

http://www.jsqmd.com/news/738577/

相关文章:

  • 从NCBI下载fna到画出进化树:一条龙完成细菌泛基因组分析(Prokka+Roary实战)
  • 用Python复刻经典AI实验:手把手教你实现一个动物识别专家系统
  • Source Han Serif CN:7字重开源宋体终极解决方案
  • 对比直接使用厂商API体验Taotoken在计费透明性上的优势
  • 契约式AI编程框架:基于OpenClaw与Codex的可验证开发流水线设计
  • 从一条CAN报文讲起:手把手带你用Python脚本模拟UDS 3E服务,实现ECU会话保活
  • NCL30000 LED驱动设计:CrM模式与漏感优化实践
  • VMware VSAN集群关机重启,我踩过的那些坑(附7.0U3版本功能实测)
  • 告别杂乱桌面!用Start11在Win11上复活全屏磁贴菜单的保姆级教程
  • 终极指南:3步免费绕过iOS 15-16激活锁的完整教程
  • Jmeter计数器配置全解析:从‘线程组迭代重置’到‘用户独立跟踪’的完整测试流程搭建
  • 别只盯着顶刊!这些Q1/Q2的医学图像处理SCI期刊,或许更适合你‘上岸’
  • Equalizer APO完整指南:如何免费获得专业级Windows音频均衡效果
  • 分期乐用户福音:支付宝立减金快速回收的超简单方法 - 团团收购物卡回收
  • 3分钟掌握SignatureTools:安卓开发者必备的图形化签名神器
  • 终极Obsidian模板指南:30分钟搭建你的Zettelkasten知识库系统
  • 从STM32F103到GD32F103:一个真实项目移植的完整避坑记录(含源码)
  • 长期运行项目观察Taotoken服务稳定性与容灾切换的实际表现
  • 高速运放建立时间测量的采样保持技术解析
  • 别再被‘天价’吓退!一文看懂Autosar免费标准与商用工具链的真正区别
  • 在树莓派4B(ARM64)上搞定PyQt5:从源码编译到解决Qt::ItemDataRole报错
  • Vite项目上线后,老板说IE11打不开?手把手教你用@vitejs/plugin-legacy搞定浏览器兼容
  • 2026年5月台州装修公司品质与报价的博弈:五家装企“质价比”硬核横评 - 疯一样的风
  • OpenCV图像处理小妙招:用自适应直方图均衡化(CLAHE)拯救你的背光/过曝照片
  • 保姆级教程:手把手教你配置华为Atlas200的AIPP,搞定YUV转BGR图像预处理
  • Claws Mail社交插件开发:Fediverse集成与本地信息聚合实践
  • 还在等什么?团团收快速回收分期乐支付宝立减金的技巧都在这里! - 团团收购物卡回收
  • WorkshopDL完整指南:无需Steam客户端下载创意工坊模组的终极方案
  • 量子测量反馈控制原理与IBM Quantum实验实践
  • 2025届学术党必备的五大降重复率助手解析与推荐