SpringBoot中怎样配置多数据源

发布时间:2021-08-03 15:36:47 作者:Leah
来源:亿速云 阅读:194
# 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多数据源核心原理

SpringBoot自动配置的数据源机制基于DataSourceAutoConfiguration,实现多数据源需要突破几个技术关键点:

  1. 自动配置覆盖
    通过@Primary注解标记主数据源,禁用默认的自动配置
@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class
})
  1. AbstractRoutingDataSource
    Spring提供的抽象路由数据源,通过determineCurrentLookupKey()方法动态选择数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceKey();
    }
}
  1. 事务管理扩展
    需要为每个数据源配置独立的事务管理器,并通过@Transactional注解指定

  2. MyBatis/JPA集成
    需要为每个数据源创建独立的SqlSessionFactory或EntityManagerFactory

  3. 连接池管理
    每个数据源应使用独立的连接池配置(如HikariCP、Druid)

SpringBoot中怎样配置多数据源

三、基于AbstractRoutingDataSource的实现方案

3.1 基础环境准备

  1. 添加Maven依赖:
<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>
  1. 配置文件application.yml:
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

3.2 数据源配置类

@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;
    }
}

3.3 动态数据源切换

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();
    }
}

3.4 事务管理配置

@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);
    }
}

四、多数据源与MyBatis集成

4.1 MyBatis配置分离

@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);
    }
}

4.2 多数据源Mapper扫描

// 主数据源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);
    }
}

五、Spring Data JPA多数据源配置

5.1 JPA实体管理器配置

@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;
    }
}

5.2 事务管理器配置

@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);
    }
}

六、多数据源与分布式事务

6.1 JTA实现方案

@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);
    }
}

6.2 Seata集成方案

  1. 添加Seata依赖:
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.5.1</version>
</dependency>
  1. 配置Seata数据源代理:
@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) {
        // 动态数据源配置
    }
}

七、多数据源性能优化

7.1 连接池配置

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

7.2 读写分离实现

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) {
        // 自动路由到主库
    }
}

八、常见问题解决方案

  1. 事务失效问题

    • 确保@Transactional注解的方法被Spring代理
    • 检查事务管理器配置是否正确
  2. 数据源切换不生效

    • 检查AOP执行顺序,确保数据源切换先于事务开启
    • 添加@Order(Ordered.HIGHEST_PRECEDENCE)到切面类
  3. 连接泄漏问题

    • 确保每次操作后调用DataSourceContextHolder.clear()
    • 配置Druid的removeAbandoned参数
  4. MyBatis缓存冲突

    • 为不同数据源配置独立的缓存实例
    • 在Mapper接口添加@CacheNamespace注解
  5. 分布式事务超时

    • 调整Seata的global.transaction.timeout参数
    • 优化业务逻辑减少事务执行时间

九、生产环境最佳实践

  1. 监控与告警

    • 集成Prometheus监控各数据源连接池状态
    • 配置关键指标告警(活跃连接数、等待连接数)
  2. 故障转移策略

    • 实现数据源健康检查机制
    • 配置备用数据源自动切换
  3. 性能调优

    • 根据压测结果调整连接池参数
    • 启用P6Spy监控SQL执行性能
  4. 安全规范

    • 使用Vault管理数据库凭据
    • 实现敏感数据加密存储
  5. 文档规范

    • 维护数据源切换决策矩阵
    • 记录各业务场景使用的事务策略

十、总结与展望

本文详细介绍了SpringBoot多数据源的完整实现方案,关键要点包括:

  1. 通过AbstractRoutingDataSource实现动态路由
  2. 结合AOP实现声明式数据源切换
  3. 多事务管理器的精细控制
  4. 与主流ORM框架的集成方案
  5. 分布式事务的解决方案

未来发展趋势:

  1. 云原生数据访问
    随着Service Mesh的普及,数据访问可能下沉到基础设施层

  2. 智能路由算法
    基于机器学习实现负载敏感的自动路由决策

  3. Serverless数据库集成
    服务器架构下的多数据源管理新范式

  4. 量子数据库连接
    未来量子计算环境下的新型数据源管理模式

最佳实践建议:对于新项目,建议从简单方案开始,随着业务复杂度增加逐步演进架构。大型系统可考虑使用ShardingSphere等专业中间件。

点击查看完整示例代码 “`

注:本文为技术方案概述,实际实施时需根据具体业务场景调整配置。由于篇幅限制,部分实现细节未完全展开,建议结合官方文档和示例代码进行实践。

推荐阅读:
  1. springboot 基于mybatis如何实现配置多数据源
  2. SpringBoot如何配置MongoDB多数据源

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

springboot

上一篇:SpringBoot中怎么实现依赖管理

下一篇:如何解决某些HTML字符打不出来的问题

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》