# 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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。