Spring Boot多数据源配置与事务管理(2025终极指南)
在现代企业应用中,集成多个数据库是常见需求。本文将深入探讨Spring Boot中多数据源的配置策略,并解决分布式事务管理的难题,提供生产级解决方案。
一、多数据源应用场景
二、基础配置:双数据源实现
1. 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- 多数据源核心依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.1.3</version>
</dependency>
</dependencies>
2. 配置数据源
spring:
datasource:
dynamic:
primary: master # 默认数据源
strict: true # 严格匹配数据源
datasource:
master:
url: jdbc:mysql://master-db:3306/db1
username: root
password: master123
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://slave-db:3306/db2
username: readuser
password: slave123
driver-class-name: com.mysql.cj.jdbc.Driver
report:
url: jdbc:mysql://report-db:3306/report_db
username: reportuser
password: report123
driver-class-name: com.mysql.cj.jdbc.Driver
三、数据源路由配置
1. 注解驱动数据源切换
@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class DataSourceConfig {
/**
* 主库数据源
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 从库数据源
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 报表库数据源
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.report")
public DataSource reportDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源路由
*/
@Bean
public DataSource dynamicDataSource(
@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource,
@Qualifier("reportDataSource") DataSource reportDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource);
targetDataSources.put("slave", slaveDataSource);
targetDataSources.put("report", reportDataSource);
DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
}
2. 数据源切面
@Aspect
@Component
@Order(-1) // 高优先级
public class DataSourceAspect {
/**
* 前置切换数据源
*/
@Before("@annotation(targetDataSource))")
public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {
String dsKey = targetDataSource.value();
if (!DynamicDataSourceContextHolder.containsDataSource(dsKey)) {
throw new DataSourceNotFoundException("数据源[" + dsKey + "]不存在");
}
DynamicDataSourceContextHolder.setDataSourceKey(dsKey);
}
/**
* 后置清除数据源
*/
@After("@annotation(targetDataSource))")
public void clearDataSource(JoinPoint point, TargetDataSource targetDataSource) {
DynamicDataSourceContextHolder.clearDataSourceKey();
}
}
3. 自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value() default "master";
}
四、事务管理策略
1. 单数据源事务管理
@Service
public class UserService {
@TargetDataSource("master")
@Transactional(rollbackFor = Exception.class)
public void createUser(User user) {
// 主库操作
userRepository.save(user);
// 日志记录到从库
logService.addLog("用户创建", user.getId());
}
}
2. 分布式事务管理(XA协议)
添加Atomikos依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
XA数据源配置
@Bean
public DataSource masterDataSource() {
MysqlXADataSource xaDataSource = new MysqlXADataSource();
xaDataSource.setUrl(env.getProperty("master.url"));
xaDataSource.setUser(env.getProperty("master.username"));
xaDataSource.setPassword(env.getProperty("master.password"));
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
dataSource.setXaDataSource(xaDataSource);
dataSource.setUniqueResourceName("masterXADB");
return dataSource;
}
JTA事务管理器
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public UserTransaction userTransaction() throws SystemException {
UserTransactionImp userTransaction = new UserTransactionImp();
userTransaction.setTransactionTimeout(300);
return userTransaction;
}
@Bean
public TransactionManager atomikosTransactionManager() {
UserTransactionManager transactionManager = new UserTransactionManager();
transactionManager.setForceShutdown(false);
return transactionManager;
}
@Bean
public PlatformTransactionManager transactionManager(
@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) throws Exception {
return new JtaTransactionManager(
userTransaction(),
atomikosTransactionManager()
);
}
}
3. 分布式事务注解
@TargetDataSource("master")
@Transactional(value = "transactionManager", rollbackFor = Exception.class)
public void crossDatabaseOperation() {
// 操作主库
userRepository.save(new User());
// 操作从库
reportRepository.generateReport();
// 如果任意操作失败,两个数据源都会回滚
}
五、性能优化与生产实践
1. 连接池优化配置
spring:
datasource:
master:
# HikariCP优化
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
slave:
# Druid优化
druid:
max-active: 15
min-idle: 3
initial-size: 3
max-wait: 3000
2. 多数据源监控方案
3. 读写分离路由策略
public class ReadWriteSplitRouting extends AbstractRoutingDataSource {
private static final ThreadLocal<Boolean> readOnlyFlag =
ThreadLocal.withInitial(() -> false);
public static void markReadOnly() {
readOnlyFlag.set(true);
}
public static void clearReadOnly() {
readOnlyFlag.remove();
}
@Override
protected Object determineCurrentLookupKey() {
return readOnlyFlag.get() ? "slave" : "master";
}
}
六、常见问题解决方案
1. 事务失效场景
2. 数据源切换失败排查
@GetMapping("/debug")
public String debugDataSource() {
StringBuilder sb = new StringBuilder();
// 检查数据源配置
sb.append("已配置数据源: ")
.append(DynamicDataSourceContextHolder.getDataSourceKeys());
// 当前线程数据源
sb.append("\n当前数据源: ")
.append(DynamicDataSourceContextHolder.getDataSourceKey());
// 事务状态
sb.append("\n事务状态: ")
.append(TransactionSynchronizationManager.isActualTransactionActive());
return sb.toString();
}
七、多数据源最佳实践
1. 分库分表示例
public class DataSourceRouter {
// 按用户ID分片
public static String getDataSourceKey(Long userId) {
int slot = userId % 4;
return "shard_" + slot;
}
// 在切面中动态选择
@Before("@annotation(router))")
public void routeByUserId(JoinPoint point, ShardingRouter router) {
Object arg = point.getArgs()[0];
if (arg instanceof User) {
Long userId = ((User)arg).getId();
String dsKey = getDataSourceKey(userId);
// 设置数据源...
}
}
}
2. 主从延迟解决方案
@TargetDataSource("master")
public void writeOperation() {
// 主库写入操作
// 强制后续操作走主库
ForceMasterHelper.forceMaster();
}
// 强制主库工具
public class ForceMasterHelper {
private static final ThreadLocal<Boolean> forceMaster =
ThreadLocal.withInitial(() -> false);
public static void forceMaster() {
forceMaster.set(true);
}
public static boolean isForceMaster() {
return forceMaster.get();
}
public static void clear() {
forceMaster.remove();
}
}
八、总结与进阶
分层架构图示
推荐技术组合
场景 | 推荐方案 | 特点 |
---|---|---|
简单读写分离 | Dynamic Datasource | 轻量、易用 |
多类型数据库 | Atomikos JTA | 强一致性 |
超大规模分片 | ShardingSphere | 分库分表专业方案 |
云原生环境 | Seata AT模式 | 无侵入、支持TCC |
生产环境建议:对于大部分企业应用,推荐使用 动态数据源+分布式事务代理 方案。某电商平台采用该方案后,成功支撑双11期间2亿+订单的处理,数据库负载下降40%,事务失败率低于0.001%。