
1. 项目概述作为一名常年与数据库打交道的Java开发者我深刻理解多数据源配置在实际项目中的重要性。当系统需要同时连接多个数据库比如主从分离、分库分表、多租户等场景时传统的单数据源方案就显得捉襟见肘。MyBatis-Plus作为MyBatis的增强工具配合dynamic-datasource-spring-boot-starter组件可以优雅地解决这个问题。这个方案的核心价值在于零侵入性无需修改原有MyBatis-Plus代码动态切换通过注解即可实现运行时数据源切换配置简单Spring Boot风格的配置方式功能完善支持事务、读写分离等高级特性我最近在电商项目中就遇到了这样的需求需要同时操作业务数据库和日志数据库。经过多种方案对比最终选择了MyBatis-Plus多数据源方案实测下来非常稳定。下面就把我的配置过程和踩坑经验分享给大家。2. 环境准备与依赖配置2.1 版本匹配要点在开始之前版本兼容性是首要考虑的问题。根据我的经验版本不匹配会导致90%的配置问题。以下是经过验证的稳定版本组合properties spring-boot.version2.7.3/spring-boot.version mybatis-plus.version3.5.1/mybatis-plus.version dynamic-datasource.version3.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 groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId version${spring-boot.version}/version /dependency !-- MyBatis-Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version${mybatis-plus.version}/version /dependency !-- 多数据源核心 -- dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version${dynamic-datasource.version}/version /dependency !-- 数据库驱动以MySQL为例 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.28/version /dependency /dependencies3. 多数据源配置详解3.1 基础配置在application.yml中配置多数据源spring: datasource: dynamic: primary: master # 设置默认数据源 strict: false # 是否严格匹配数据源默认false datasource: master: url: jdbc:mysql://localhost:3306/master_db?useSSLfalse username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/slave_db?useSSLfalse 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 方法调用链中的注意事项在方法调用链中数据源切换遵循以下规则外层方法没有DS注解时内层方法注解生效外层方法有DS注解时内层注解不生效事务方法中切换数据源需要特殊处理后面会讲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() { // 这里的事务可能不会按预期工作 }这是因为Spring事务基于AOP实现事务切面在数据源切换切面之前执行导致事务内使用的数据源不正确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注解但数据源没有切换排查步骤检查注解是否写在接口上应该写在实现类检查方法是否是public非public方法注解不生效检查是否被同类方法调用自调用注解不生效7.2 事务不生效现象事务回滚失败解决方案使用DSTransactional替代Transactional或者确保事务方法的数据源与DS一致7.3 性能问题现象系统变慢优化建议为不同数据源配置独立的连接池参数监控连接泄漏合理设置超时时间8. 最佳实践总结经过多个项目的实践我总结了以下经验命名规范数据源名称要有意义如order_master、log_slave等监控配置集成Druid监控每个数据源的状态压测验证上线前模拟多数据源并发场景降级方案主库不可用时自动降级到从库文档记录团队内部维护数据源使用规范最后分享一个实用技巧在开发环境可以使用H2内存数据库作为备选数据源避免因数据库服务不可用阻塞开发spring: datasource: dynamic: datasource: dev_mem: url: jdbc:h2:mem:testdb username: sa password: driver-class-name: org.h2.Driver