您好,登录后才能下订单哦!
# SpringBoot中怎样配置多数据源
## 目录
- [一、多数据源应用场景](#一多数据源应用场景)
- [二、SpringBoot多数据源核心原理](#二springboot多数据源核心原理)
- [三、基于AbstractRoutingDataSource的实现方案](#三基于abstractroutingdatasource的实现方案)
- [3.1 基础环境准备](#31-基础环境准备)
- [3.2 数据源配置类](#32-数据源配置类)
- [3.3 动态数据源切换](#33-动态数据源切换)
- [3.4 事务管理配置](#34-事务管理配置)
- [四、多数据源与MyBatis集成](#四多数据源与mybatis集成)
- [4.1 MyBatis配置分离](#41-mybatis配置分离)
- [4.2 多数据源Mapper扫描](#42-多数据源mapper扫描)
- [五、Spring Data JPA多数据源配置](#五spring-data-jpa多数据源配置)
- [5.1 JPA实体管理器配置](#51-jpa实体管理器配置)
- [5.2 事务管理器配置](#52-事务管理器配置)
- [六、多数据源与分布式事务](#六多数据源与分布式事务)
- [6.1 JTA实现方案](#61-jta实现方案)
- [6.2 Seata集成方案](#62-seata集成方案)
- [七、多数据源性能优化](#七多数据源性能优化)
- [7.1 连接池配置](#71-连接池配置)
- [7.2 读写分离实现](#72-读写分离实现)
- [八、常见问题解决方案](#八常见问题解决方案)
- [九、生产环境最佳实践](#九生产环境最佳实践)
- [十、总结与展望](#十总结与展望)
## 一、多数据源应用场景
在现代企业级应用开发中,多数据源配置已成为常见需求,主要应用场景包括:
1. **业务数据隔离**
不同业务模块使用独立数据库实例,例如用户中心、订单系统分别部署在不同MySQL实例
2. **读写分离架构**
主库负责写操作,多个从库处理读请求,如电商系统商品查询与订单写入分离
3. **多数据库类型混合**
事务数据使用MySQL,文档数据存储MongoDB,缓存使用Redis的多存储引擎组合
4. **分库分表中间件集成**
配合ShardingSphere等中间件实现水平分库时的数据源路由
5. **多租户SaaS系统**
每个租户使用独立数据库实例,通过数据源动态切换实现租户隔离
6. **数据迁移与同步**
双写场景下需要同时操作新旧两个数据库系统
7. **异构数据源整合**
需要同时访问传统关系型数据库和新型时序数据库(如InfluxDB)的场景
```java
// 典型的多租户数据源选择逻辑示例
public class TenantDataSourceSelector {
public static String determineDataSourceKey() {
String tenantId = TenantContext.getCurrentTenant();
return "ds_" + tenantId;
}
}
SpringBoot自动配置的数据源机制基于DataSourceAutoConfiguration
,实现多数据源需要突破几个技术关键点:
@Primary
注解标记主数据源,禁用默认的自动配置@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class
})
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceKey();
}
}
事务管理扩展
需要为每个数据源配置独立的事务管理器,并通过@Transactional
注解指定
MyBatis/JPA集成
需要为每个数据源创建独立的SqlSessionFactory或EntityManagerFactory
连接池管理
每个数据源应使用独立的连接池配置(如HikariCP、Druid)
<dependencies>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 根据实际需要添加MyBatis或JPA依赖 -->
</dependencies>
spring:
datasource:
master:
jdbc-url: jdbc:mysql://localhost:3306/master_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
jdbc-url: jdbc:mysql://localhost:3307/slave_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource dynamicDataSource(DataSource masterDataSource,
DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource);
targetDataSources.put("slave", slaveDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
return dynamicDataSource;
}
}
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setDataSourceKey(String key) {
CONTEXT.set(key);
}
public static String getDataSourceKey() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
// 使用AOP实现注解式切换
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "master";
}
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(ds)")
public void beforeSwitchDataSource(DataSource ds) {
DataSourceContextHolder.setDataSourceKey(ds.value());
}
@After("@annotation(ds)")
public void afterSwitchDataSource(DataSource ds) {
DataSourceContextHolder.clear();
}
}
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DynamicDataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 多事务管理器配置示例
@Bean
public PlatformTransactionManager masterTxManager(@Qualifier("masterDataSource") DataSource ds) {
return new DataSourceTransactionManager(ds);
}
@Bean
public PlatformTransactionManager slaveTxManager(@Qualifier("slaveDataSource") DataSource ds) {
return new DataSourceTransactionManager(ds);
}
}
@Configuration
@MapperScan(basePackages = "com.example.mapper.master",
sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterMyBatisConfig {
@Bean
public SqlSessionFactory masterSqlSessionFactory(
@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/master/*.xml"));
return sessionFactory.getObject();
}
@Bean
public SqlSessionTemplate masterSqlSessionTemplate(
@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
// 主数据源Mapper接口
@Mapper
public interface MasterUserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(Long id);
}
// 从数据源Mapper接口
@Mapper
public interface SlaveOrderMapper {
@Select("SELECT * FROM orders WHERE user_id = #{userId}")
List<Order> selectByUserId(Long userId);
}
// 使用示例
@Service
public class BusinessService {
private final MasterUserMapper masterUserMapper;
private final SlaveOrderMapper slaveOrderMapper;
@DataSource("slave")
public List<Order> getUserOrders(Long userId) {
User user = masterUserMapper.selectById(userId);
return slaveOrderMapper.selectByUserId(userId);
}
}
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.repository.primary",
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryJpaConfig {
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("primaryDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.example.entity.primary")
.persistenceUnit("primaryPersistenceUnit")
.properties(jpaProperties())
.build();
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
props.put("hibernate.hbm2ddl.auto", "update");
return props;
}
}
@Configuration
public class JpaTransactionConfig {
@Primary
@Bean
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryEntityManagerFactory") EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
@Bean
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
@Configuration
@EnableTransactionManagement
public class JtaConfig {
@Bean
public UserTransaction userTransaction() throws Throwable {
UserTransactionImp userTransaction = new UserTransactionImp();
userTransaction.setTransactionTimeout(300);
return userTransaction;
}
@Bean
public TransactionManager atomikosTransactionManager() {
UserTransactionManager manager = new UserTransactionManager();
manager.setForceShutdown(false);
return manager;
}
@Bean
public JtaTransactionManager transactionManager(
UserTransaction userTransaction,
TransactionManager atomikosTransactionManager) {
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.1</version>
</dependency>
@Configuration
public class SeataConfig {
@Bean
public DataSourceProxy masterDataSourceProxy(@Qualifier("masterDataSource") DataSource ds) {
return new DataSourceProxy(ds);
}
@Bean
public DataSourceProxy slaveDataSourceProxy(@Qualifier("slaveDataSource") DataSource ds) {
return new DataSourceProxy(ds);
}
@Bean
@Primary
public DataSource dynamicDataSource(DataSourceProxy masterProxy,
DataSourceProxy slaveProxy) {
// 动态数据源配置
}
}
spring:
datasource:
master:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
slave:
hikari:
maximum-pool-size: 30
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
public class ReadWriteDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if(isReadOnly) {
return "slave";
}
return "master";
}
}
// 使用示例
@Service
public class ProductService {
@Transactional(readOnly = true)
public Product getProduct(Long id) {
// 自动路由到从库
}
@Transactional
public void updateProduct(Product product) {
// 自动路由到主库
}
}
事务失效问题
数据源切换不生效
@Order(Ordered.HIGHEST_PRECEDENCE)
到切面类连接泄漏问题
MyBatis缓存冲突
@CacheNamespace
注解分布式事务超时
监控与告警
故障转移策略
性能调优
安全规范
文档规范
本文详细介绍了SpringBoot多数据源的完整实现方案,关键要点包括:
未来发展趋势:
云原生数据访问
随着Service Mesh的普及,数据访问可能下沉到基础设施层
智能路由算法
基于机器学习实现负载敏感的自动路由决策
Serverless数据库集成
无服务器架构下的多数据源管理新范式
量子数据库连接
未来量子计算环境下的新型数据源管理模式
最佳实践建议:对于新项目,建议从简单方案开始,随着业务复杂度增加逐步演进架构。大型系统可考虑使用ShardingSphere等专业中间件。
点击查看完整示例代码 “`
注:本文为技术方案概述,实际实施时需根据具体业务场景调整配置。由于篇幅限制,部分实现细节未完全展开,建议结合官方文档和示例代码进行实践。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。