Spring Boot多数据源配置实战指南
在 Spring Boot 中实现多数据源拆分,需根据业务需求(如读写分离、模块隔离、多租户等)选择合适的方案。以下是核心思路及实现方式:
🔧 一、配置多个数据源
在 application.yml
中为每个数据源定义独立配置:
spring:datasource:primary:url: jdbc:mysql://localhost:3306/db1username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driversecondary:url: jdbc:mysql://localhost:3306/db2username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
通过 @Configuration
类创建多个 DataSource
Bean:
@Configuration
public class DataSourceConfig {// 主数据源@Bean@ConfigurationProperties(prefix = "spring.datasource.primary")public DataSource primaryDataSource() {return DataSourceBuilder.create().build();}// 从数据源@Bean@ConfigurationProperties(prefix = "spring.datasource.secondary")public DataSource secondaryDataSource() {return DataSourceBuilder.create().build();}
}
🔀 二、动态数据源切换(核心方案)
1. 继承 AbstractRoutingDataSource
自定义路由类,根据线程上下文选择数据源:
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSourceType(); // 返回数据源标识(如"primary")}
}
2. 管理数据源上下文
通过 ThreadLocal
存储当前线程的数据源标识:
public class DataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();public static void setDataSourceType(String dsType) {contextHolder.set(dsType);}public static String getDataSourceType() {return contextHolder.get();}public static void clear() {contextHolder.remove();}
}
3. 通过 AOP 自动切换
使用自定义注解(如 @DS("secondary")
)标注方法,在切面中动态切换数据源:
@Aspect
@Component
public class DataSourceAspect {@Before("@annotation(ds)")public void switchDataSource(JoinPoint point, DS ds) {DataSourceContextHolder.setDataSourceType(ds.value()); }@After("@annotation(ds)")public void restoreDataSource(DS ds) {DataSourceContextHolder.clear(); }
}
📦 三、分包管理方案(替代方案)
将不同数据源的 DAO/Mapper 分别放入独立包路径,并为每个包配置独立的 SqlSessionFactory
:
@Configuration
@MapperScan(basePackages = "com.example.dao.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {@Beanpublic SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);return bean.getObject();}
}
适用场景:业务模块边界清晰、数据源切换频率低。
⚠️ 四、事务管理
多数据源需单独配置事务管理器,避免跨库事务问题:
@Bean
public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);
}
注意:跨数据源操作如需强一致性,需引入分布式事务(如 Atomikos)。
🎯 五、适用场景
场景 | 方案推荐 |
---|---|
读写分离 | 动态路由 + AOP 注解切换 |
业务模块隔离 | 分包管理 + 独立事务管理器 |
多租户 | 动态路由(按租户标识切换) |
分库分表 | ShardingSphere 集成 |
💡 关键注意事项
- 避免循环依赖:配置类中需明确
@Primary
数据源。 - 连接池配置:为每个
DataSource
单独设置连接池参数(如 HikariCP)。 - 性能监控:多数据源需独立监控 SQL 执行性能。
- 跨库查询限制:尽量避免跨库关联查询,可通过冗余字段或业务层拼接解决。
动态路由方案灵活性高但复杂度稍高,适合高频切换场景;分包方案更简单直观,适合模块化架构。根据实际业务负载和团队技术储备选择。