多数据源
多数据源指在同一应用中同时连接多个数据库,常见场景:业务库 + 日志库、主库 + 从库(读写分离)、多租户隔离。
方案对比
| 方案 | 适用场景 | 侵入性 |
|---|---|---|
手动配置多个 DataSource Bean | 数据源固定、少量 | 高(需为每个库配置全套) |
AbstractRoutingDataSource | 动态切换、有限数量 | 中(自定义路由逻辑) |
| dynamic-datasource-spring-boot-starter | 通用多数据源 | 低(注解驱动,推荐) |
| ShardingSphere | 分库分表 + 读写分离 | 低(配置驱动) |
方案一:手动配置多 DataSource
适合两个固定数据源(如主库 + 报表库)。
# application.yml
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/main_db
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
report:
url: jdbc:mysql://localhost:3306/report_db
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver@Configuration
public class DataSourceConfig {
// 主数据源(@Primary 保证默认注入)
@Bean
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
// 报表数据源
@Bean
@ConfigurationProperties("spring.datasource.report")
public DataSource reportDataSource() {
return DataSourceBuilder.create().build();
}
// 为报表库单独配置 JdbcTemplate
@Bean
public JdbcTemplate reportJdbcTemplate(
@Qualifier("reportDataSource") DataSource ds) {
return new JdbcTemplate(ds);
}
}// Service 中通过 @Qualifier 注入指定库的 JdbcTemplate
@Service
public class ReportService {
private final JdbcTemplate reportJdbc;
public ReportService(
@Qualifier("reportJdbcTemplate") JdbcTemplate reportJdbc) {
this.reportJdbc = reportJdbc;
}
}多数据源下的 JPA 配置
每个数据源需要独立的 EntityManagerFactory 和 TransactionManager:
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.report.repo",
entityManagerFactoryRef = "reportEntityManagerFactory",
transactionManagerRef = "reportTransactionManager"
)
public class ReportJpaConfig {
@Bean
public LocalContainerEntityManagerFactoryBean reportEntityManagerFactory(
@Qualifier("reportDataSource") DataSource ds,
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(ds)
.packages("com.example.report.entity")
.build();
}
@Bean
public PlatformTransactionManager reportTransactionManager(
@Qualifier("reportEntityManagerFactory")
EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}事务注解需指定对应的 TransactionManager,详见 事务管理:
@Transactional("reportTransactionManager")
public void saveReport(Report report) { ... }方案二:dynamic-datasource-spring-boot-starter(推荐)
baomidou/dynamic-datasource 是目前最主流的多数据源方案,支持注解切换、分组路由、连接池隔离。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>spring:
datasource:
dynamic:
primary: master # 默认数据源
strict: false # 未匹配时是否报错
datasource:
master:
url: jdbc:mysql://localhost:3306/main_db
username: root
password: root
slave1:
url: jdbc:mysql://localhost:3307/main_db
username: root
password: root
report:
url: jdbc:mysql://localhost:3306/report_db
username: root
password: root// @DS 注解指定数据源,可加在类或方法上,方法优先级高于类
@Service
@DS("master")
public class UserService {
public User findById(Long id) { ... } // 使用 master
@DS("slave1")
public List<User> findAll() { ... } // 使用 slave1
}
@Service
@DS("report")
public class ReportService {
// 所有方法使用 report 数据源
}方案三:AbstractRoutingDataSource(自定义路由)
适合需要完全控制路由逻辑的场景:
// 1. 线程本地存储当前数据源标识
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void set(String key) { CONTEXT.set(key); }
public static String get() { return CONTEXT.get(); }
public static void clear() { CONTEXT.remove(); }
}
// 2. 实现路由逻辑
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.get();
}
}
// 3. 注册路由数据源
@Configuration
public class RoutingDataSourceConfig {
@Bean
public DataSource routingDataSource(
@Qualifier("masterDs") DataSource master,
@Qualifier("slaveDs") DataSource slave) {
Map<Object, Object> targets = new HashMap<>();
targets.put("master", master);
targets.put("slave", slave);
RoutingDataSource routing = new RoutingDataSource();
routing.setDefaultTargetDataSource(master);
routing.setTargetDataSources(targets);
return routing;
}
}
// 4. 配合 AOP 自动切换(见 读写分离)详见 读写分离 中基于 AOP 的自动路由实现。
事务注意事项
多数据源下跨库事务无法用单个 @Transactional 保证原子性,需使用分布式事务方案:
| 方案 | 特点 |
|---|---|
| Seata AT 模式 | 自动补偿,对代码无侵入,性能较好 |
| Seata TCC 模式 | 手动编写 Try/Confirm/Cancel,强一致 |
| 本地消息表 | 最终一致,依赖消息队列 |
| 避免跨库事务 | 业务设计上拆分,单库内保证事务 |
同一数据源内的事务保持不变,详见 事务管理。