logo

SpringBoot多数据源配置与避坑指南

作者:carzy2026.11.07 02:28浏览量:0

简介:本文从适用场景出发,系统讲解SpringBoot多数据源的配置流程与核心原理,涵盖依赖引入、参数配置、数据源注册、会话工厂构建及Mapper绑定等关键环节,并结合事务管理与连接池调优给出实践建议,帮助开发者避开常见误区。

SpringBoot多数据源配置与避坑指南

一、先想清楚:你真的需要多数据源吗

在动手写配置之前,有必要先泼一盆冷水:大多数业务系统其实并不需要多数据源。很多开发者刚掌握这项技能就急于在项目中落地,结果反而把架构搞得复杂无比。多数据源并不是“技术先进”的标志,而是一种在特定约束下不得不做的妥协。它天然带来事务跨库失效、连接资源翻倍、维护成本上升等一系列问题。因此,是否引入多数据源,应该由业务场景决定,而不是由技术偏好决定。

结合实践经验,真正需要多数据源的场景主要有以下三类:

  1. 跨库关联查询:业务上需要同时读取两个甚至多个独立数据库的数据,且没有中间件承担数据同步职责。例如订单库与用户库物理分离,但报表需要联合查询。
  2. 新旧系统并行过渡:老系统迁移期间,需要同时读写新库与老库,以便做数据校验、灰度切流或回滚兜底。
  3. 轻量级读写分离:希望把读请求分散到从库,但团队没有条件引入成熟的读写分离中间件,只能通过应用层手动路由。

如果只是单库业务,强行上多数据源只会徒增复杂度。事务管理、连接池分配、SQL 路由判断都会变成新的故障点,得不偿失。

二、整体配置思路

多数据源的配置本质上是一次“手动接管自动装配”的过程。SpringBoot 默认只装配一个数据源,当我们需要多个时,就必须自己声明多个数据源 Bean,并让 MyBatis 为每个数据源分别构建独立的会话工厂,最后通过包路径扫描把不同的 Mapper 绑定到对应的数据源上。

整体流程可以概括为五步:

  1. 引入必要依赖;
  2. 在配置文件中声明多套数据源参数;
  3. 编写自定义配置类,注册多个数据源 Bean;
  4. 为每个数据源构建独立的会话工厂;
  5. 通过 Mapper 扫描路径完成绑定。

只要这五步走通,多数据源就能稳定运行。

三、第一步:依赖准备

不少人在配置阶段频繁报错,根源往往不是配置写错,而是依赖缺失。最常见的问题是缺少 JDBC 或持久层框架依赖,导致启动时直接提示找不到数据源相关类。因此第一步务必把依赖补齐。

下面是经过验证的精简依赖组合,适用于 SpringBoot 2.7.x 版本,升级版本时只需调整版本号即可:

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.7.14</version>
  5. <relativePath/>
  6. </parent>
  7. <dependencies>
  8. <!-- Web 核心 -->
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-web</artifactId>
  12. </dependency>
  13. <!-- 持久层启动器 -->
  14. <dependency>
  15. <groupId>org.mybatis.spring.boot</groupId>
  16. <artifactId>mybatis-spring-boot-starter</artifactId>
  17. <version>2.2.2</version>
  18. </dependency>
  19. <!-- 数据库驱动 -->
  20. <dependency>
  21. <groupId>mysql</groupId>
  22. <artifactId>mysql-connector-java</artifactId>
  23. <scope>runtime</scope>
  24. </dependency>
  25. <!-- 连接池,可选,默认连接池亦可 -->
  26. <dependency>
  27. <groupId>com.alibaba</groupId>
  28. <artifactId>druid-spring-boot-starter</artifactId>
  29. <version>1.2.16</version>
  30. </dependency>
  31. </dependencies>

连接池并非必须使用某一特定实现,默认的连接池同样可以工作。选择哪一款,应结合监控能力、连接治理需求与团队熟悉度综合考虑。

四、第二步:配置文件声明多套数据源

在配置文件中,需要为每个数据源分别声明 URL、用户名、密码、驱动类等参数。建议使用清晰的前缀区分,例如 spring.datasource.primary 与 spring.datasource.secondary,避免与 SpringBoot 默认的单数据源配置混淆。

  1. spring:
  2. datasource:
  3. primary:
  4. url: jdbc:mysql://127.0.0.1:3306/db_primary?useUnicode=true&characterEncoding=utf8
  5. username: root
  6. password: pass1
  7. driver-class-name: com.mysql.cj.jdbc.Driver
  8. secondary:
  9. url: jdbc:mysql://127.0.0.1:3306/db_secondary?useUnicode=true&characterEncoding=utf8
  10. username: root
  11. password: pass2
  12. driver-class-name: com.mysql.cj.jdbc.Driver

需要特别注意的是,一旦自定义了数据源前缀,SpringBoot 的默认自动装配将不再生效,后续所有数据源都必须由配置类手动创建。

五、第三步:注册数据源 Bean

配置类的核心任务是创建多个数据源实例,并通过 @ConfigurationProperties 把配置文件中的参数绑定到对应实例上。同时,必须指定一个主数据源并标注 @Primary,否则在存在多个同类型 Bean 时,自动注入将无法判断该选哪一个。

  1. @Configuration
  2. public class DataSourceConfig {
  3. @Primary
  4. @Bean(name = "primaryDataSource")
  5. @ConfigurationProperties(prefix = "spring.datasource.primary")
  6. public DataSource primaryDataSource() {
  7. return DataSourceBuilder.create().build();
  8. }
  9. @Bean(name = "secondaryDataSource")
  10. @ConfigurationProperties(prefix = "spring.datasource.secondary")
  11. public DataSource secondaryDataSource() {
  12. return DataSourceBuilder.create().build();
  13. }
  14. }

这里的关键点是 Bean 名称必须与后续会话工厂中引用的名称一致,否则会出现注入失败或数据源错配的问题。

六、第四步:为每个数据源构建独立的会话工厂

MyBatis 的会话工厂负责管理 SQL 映射与执行,每个数据源都需要一个独立的工厂实例。构建时需要绑定对应的数据源,并指定 Mapper XML 文件的位置。

  1. @Configuration
  2. @MapperScan(
  3. basePackages = "com.example.mapper.primary",
  4. sqlSessionFactoryRef = "primarySqlSessionFactory"
  5. )
  6. public class PrimaryMyBatisConfig {
  7. @Bean(name = "primarySqlSessionFactory")
  8. @Primary
  9. public SqlSessionFactory primarySqlSessionFactory(
  10. @Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
  11. SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  12. bean.setDataSource(dataSource);
  13. bean.setMapperLocations(
  14. new PathMatchingResourcePatternResolver()
  15. .getResources("classpath:mapper/primary/*.xml"));
  16. return bean.getObject();
  17. }
  18. }

第二个数据源的配置类结构完全相同,只是包路径、Bean 名称与数据源引用需要对应调整。需要注意的是,@MapperScan 中的 sqlSessionFactoryRef 必须显式指定,否则可能误用主工厂,导致 SQL 被路由到错误的库。

七、第五步:通过包路径完成 Mapper 绑定

多数据源能够“自动路由”的本质,其实是包路径隔离。不同业务模块的 Mapper 接口存放在不同的包下,每个包对应的 @MapperScan 又绑定了不同的会话工厂,于是调用某个 Mapper 时,自然就走到了它所属的数据源上。这种设计简单可靠,但要求项目结构必须保持清晰的包划分,不能把不同数据源的 Mapper 混放在同一目录下。

八、事务与连接池的注意事项

多数据源场景下,声明式事务默认只能作用于单一数据源。一旦跨库操作,本地事务无法保证一致性,需要考虑补偿机制或分布式事务方案。此外,每个数据源都会维护独立的连接池,连接数应按实际负载分别配置,避免总连接数超出数据库上限。监控方面,建议为每个数据源单独采集连接活跃度、等待时长等指标,以便快速定位是哪个库成为瓶颈。

九、总结

多数据源配置并不难,难的是判断该不该用,以及用完之后如何控制复杂度。核心步骤可以概括为:依赖齐全、参数分套、数据源分 Bean、会话工厂分实例、Mapper 分包扫描。只要这五步严格执行,再配合事务与连接池的合理治理,多数据源就能在合适的场景下稳定发挥价值。

发表评论

活动