ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MyBatis-Plus多数据源配置与动态切换实战

MyBatis-Plus多数据源配置与动态切换实战

1. 项目概述

作为一名常年与数据库打交道的Java开发者,我深刻理解多数据源配置在实际项目中的重要性。当系统需要同时连接多个数据库(比如主从分离、分库分表、多租户等场景)时,传统的单数据源方案就显得捉襟见肘。MyBatis-Plus作为MyBatis的增强工具,配合dynamic-datasource-spring-boot-starter组件,可以优雅地解决这个问题。

这个方案的核心价值在于:

  • 零侵入性:无需修改原有MyBatis-Plus代码
  • 动态切换:通过注解即可实现运行时数据源切换
  • 配置简单:Spring Boot风格的配置方式
  • 功能完善:支持事务、读写分离等高级特性

我最近在电商项目中就遇到了这样的需求:需要同时操作业务数据库和日志数据库。经过多种方案对比,最终选择了MyBatis-Plus多数据源方案,实测下来非常稳定。下面就把我的配置过程和踩坑经验分享给大家。

2. 环境准备与依赖配置

2.1 版本匹配要点

在开始之前,版本兼容性是首要考虑的问题。根据我的经验,版本不匹配会导致90%的配置问题。以下是经过验证的稳定版本组合:

<properties> <spring-boot.version>2.7.3</spring-boot.version> <mybatis-plus.version>3.5.1</mybatis-plus.version> <dynamic-datasource.version>3.6.1</dynamic-datasource.version> </properties>

注意:MyBatis-Plus 3.5.x系列与Spring Boot 2.7.x兼容性最好。如果使用Spring Boot 3.x,需要对应升级MyBatis-Plus到最新版。

2.2 核心依赖引入

在pom.xml中添加以下依赖:

<dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- MyBatis-Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- 多数据源核心 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>${dynamic-datasource.version}</version> </dependency> <!-- 数据库驱动(以MySQL为例) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> </dependencies>

3. 多数据源配置详解

3.1 基础配置

在application.yml中配置多数据源:

spring: datasource: dynamic: primary: master # 设置默认数据源 strict: false # 是否严格匹配数据源,默认false datasource: master: url: jdbc:mysql://localhost:3306/master_db?useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/slave_db?useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

关键参数说明:

  • primary: 指定默认数据源
  • strict: 设为true时,未匹配到数据源会抛出异常
  • 每个数据源的配置与传统单数据源配置相同

3.2 高级配置选项

实际项目中我们通常需要更精细的控制:

spring: datasource: dynamic: hikari: connection-timeout: 30000 max-lifetime: 1800000 max-pool-size: 15 min-idle: 5 datasource: master: # ...其他配置 hikari: pool-name: masterHikariCP slave: # ...其他配置 hikari: pool-name: slaveHikariCP

这样可以为不同数据源单独配置连接池参数,优化性能。

4. 数据源切换实战

4.1 注解式切换

@DS注解是切换数据源的核心:

@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; // 使用master数据源 @DS("master") public void addUser(User user) { userMapper.insert(user); } // 使用slave数据源 @DS("slave") public User getUserById(Long id) { return userMapper.selectById(id); } }

4.2 方法调用链中的注意事项

在方法调用链中,数据源切换遵循以下规则:

  1. 外层方法没有@DS注解时,内层方法注解生效
  2. 外层方法有@DS注解时,内层注解不生效
  3. 事务方法中切换数据源需要特殊处理(后面会讲)
@Service public class OrderServiceImpl implements OrderService { @DS("master") public void createOrder(Order order) { // 这里使用master数据源 orderMapper.insert(order); // 即使方法有@DS("slave"),实际仍使用master updateStatistics(order); } @DS("slave") public void updateStatistics(Order order) { // 由于被createOrder调用,这里的slave不生效 } }

5. 事务处理技巧

5.1 多数据源事务的坑

默认情况下,@Transactional@DS注解一起使用会导致问题:

@DS("master") @Transactional public void transactionalMethod() { // 这里的事务可能不会按预期工作 }

这是因为:

  1. Spring事务基于AOP实现
  2. 事务切面在数据源切换切面之前执行
  3. 导致事务内使用的数据源不正确

5.2 解决方案

方案一:使用DSTransactional注解(推荐)

@DS("master") @DSTransactional public void safeTransactionalMethod() { // 现在事务和数据源都能正确工作 }

方案二:调整切面顺序

@Configuration public class DataSourceConfig { @Bean public DynamicDataSourceAnnotationAdvisor dynamicDataSourceAnnotationAdvisor() { DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(); advisor.setOrder(Ordered.HIGHEST_PRECEDENCE); return advisor; } }

6. 读写分离实战

6.1 配置读写分离

spring: datasource: dynamic: primary: master datasource: master: url: jdbc:mysql://master-host:3306/db username: root password: 123456 slave_1: url: jdbc:mysql://slave1-host:3306/db username: root password: 123456 slave_2: url: jdbc:mysql://slave2-host:3306/db username: root password: 123456 strategy: # 负载均衡策略 slave: round_robin # 轮询

6.2 使用策略

@Service public class UserServiceImpl implements UserService { // 写操作使用master @DS("master") public void addUser(User user) { // insert操作 } // 读操作自动负载均衡到slave @DS("slave") public User getUser(Long id) { // select操作 } }

7. 常见问题排查

7.1 数据源未切换

现象:添加了@DS注解但数据源没有切换

排查步骤

  1. 检查注解是否写在接口上(应该写在实现类)
  2. 检查方法是否是public(非public方法注解不生效)
  3. 检查是否被同类方法调用(自调用注解不生效)

7.2 事务不生效

现象:事务回滚失败

解决方案

  1. 使用@DSTransactional替代@Transactional
  2. 或者确保事务方法的数据源与@DS一致

7.3 性能问题

现象:系统变慢

优化建议

  1. 为不同数据源配置独立的连接池参数
  2. 监控连接泄漏
  3. 合理设置超时时间

8. 最佳实践总结

经过多个项目的实践,我总结了以下经验:

  1. 命名规范:数据源名称要有意义,如order_masterlog_slave
  2. 监控配置:集成Druid监控每个数据源的状态
  3. 压测验证:上线前模拟多数据源并发场景
  4. 降级方案:主库不可用时自动降级到从库
  5. 文档记录:团队内部维护数据源使用规范

最后分享一个实用技巧:在开发环境可以使用H2内存数据库作为备选数据源,避免因数据库服务不可用阻塞开发:

spring: datasource: dynamic: datasource: dev_mem: url: jdbc:h2:mem:testdb username: sa password: driver-class-name: org.h2.Driver
返回列表