Spring Boot多数据源配置与事务管理(2025终极指南)

发布于:2025-07-16 ⋅ 阅读:(12) ⋅ 点赞:(0)

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%。


网站公告

今日签到

点亮在社区的每一天
去签到