Skip to content

在 Spring Boot 中如何实现多数据源配置? ​

  • 标签:Spring Boot、后端

🏛 业务架构师 ​

Spring Boot 多数据源的标准做法是**"自动配置退位、手动装配接管":排除默认的 DataSourceAutoConfiguration,为每个数据源独立建配置类(DataSource + SqlSessionFactory/EntityManager + TransactionManager + Mapper 包路径隔离),再配合动态路由**应对更复杂场景:

java
// 主数据源:订单库(读写)
@Bean @Primary
@ConfigurationProperties("app.db.order")
DataSource orderDataSource() { ... }

// 从数据源:报表库(只读)
@Bean
@ConfigurationProperties("app.db.report")
DataSource reportDataSource() { ... }

进阶形态是 AbstractRoutingDataSource + 注解/AOP 切换(读写分离)或 ShardingSphere(分库分表)。

业务架构师视角,多数据源本质是数据架构问题,我的决策树:

  1. 先问"为什么需要":读写分离、报表与交易隔离、多租户隔离——目的决定方案,不是为了炫技;
  2. 优先考虑边界而非配置:多数场景更好的答案是按限界上下文拆服务,各自一个库(微服务的数据自治);一个应用里揉多个库,往往说明服务边界该拆而没拆;
  3. 必须管住事务边界:跨数据源没有本地事务,要么按库拆分事务边界,要么 Seata AT/TCC 或"最终一致 + 对账"——跨库一致性方案要在设计期定,不是出事故后补;
  4. 连接池分别容量规划——多数据源的连接池翻倍占用,是被容量事故教育过的点。

一句话:多数据源是技术手段,数据归属与一致性策略才是架构决策。

🏗 系统架构师 ​

实现路径:

  1. 定义多套 DataSource、TransactionManager、SqlSessionFactory,一套标 @Primary 定默认;
  2. MyBatis 按 @MapperScan 分包绑定;JPA 按 @EnableJpaRepositories 分包;
  3. 动态切换用 AbstractRoutingDataSource(ThreadLocal 上下文路由)加自定义注解 AOP 切换。

架构判断(什么时候不该用它):

  • 读写分离:应用层路由的痛点是事务内连接被绑定、扩从库要改配置——更优解是代理/中间件层(ShardingSphere-JDBC 读写分离、ProxySQL),对应用透明;
  • 跨业务域多库:先问是不是微服务拆分没做干净——不同域各自为库、各自单数据源(物理隔离),而不是一个应用塞多源;多数据源常是「分布式单体」的症状;
  • 事务边界:两个数据源没有传播行为可言,@Transactional 只管一个管理器——跨库一致性要 JTA(重)或业务补偿(我的默认)。

容量规划:每数据源独立连接池,所有池的总连接数不超过 DB 的最大连接预算。

💻 全栈开发专家 ​

分包式多数据源(订单库 + 报表库)核心:两套 DataSource + 两套事务管理器 + 分包扫描,以 MyBatis 为例:

java
@Configuration
@MapperScan(basePackages = "com.xxx.order.mapper",
            sqlSessionFactoryRef = "orderSqlSessionFactory")
public class OrderDataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.order")
    public DataSource orderDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public SqlSessionFactory orderSqlSessionFactory(DataSource ds) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(ds);
        return factory.getObject();
    }

    @Bean
    @Primary
    public DataSourceTransactionManager orderTxManager(DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
}
// report 包同理再来一套,绑定 reportDataSource,不标 @Primary

要点:事务必须显式指定管理器 @Transactional("orderTxManager"),否则默认管理器管错库,事务"看起来失效";@Primary 只标一套。JPA 场景则是两个 EntityManagerFactory + @EnableJpaRepositories 分包。

简化方案是 dynamic-datasource-spring-boot-starter(@DS("report") 注解切库),但动态切换没有跨库事务——同一方法写两个库本地事务保不住,要么拆成两次业务调用 + 补偿,要么 Seata AT。我的选型:读写分离/异构库用多数据源,强一致写重构为服务化调用 + 分布式事务。