1. 多数据源配置的核心价值与场景
在真实的企业级开发中,单一数据源往往无法满足复杂业务需求。以电商系统为例,订单数据可能存储在MySQL集群,用户行为日志写入Elasticsearch,而风控数据需要对接Oracle数据库。RuoYi-Vue-Plus作为基于SpringBoot的快速开发框架,其多数据源功能正是为解决这类异构数据访问难题而生。
我最近在实施一个政务项目时,就遇到了核心业务库与档案库分离的情况。通过配置多数据源,成功实现了业务系统与档案系统的无缝对接,整个过程比预想的要顺畅许多。下面就把这套经过实战检验的配置方案分享给大家。
2. 环境准备与基础配置
2.1 必要依赖检查
首先确保pom.xml包含关键依赖(以5.x版本为例):
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
注意:如果项目已引入mybatis-plus-boot-starter,需确认版本兼容性。我在实际项目中遇到过3.4.1版本与SpringBoot 2.6.x的冲突问题,最终升级到3.5.2解决。
2.2 配置文件详解
在application.yml中配置主从数据源:
yaml复制spring:
datasource:
dynamic:
primary: master # 默认数据源
strict: true # 严格匹配数据源
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/ry-cloud?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave1:
url: jdbc:mysql://192.168.1.100:3306/ry-report?useSSL=false
username: report_user
password: report@123
driver-class-name: com.mysql.cj.jdbc.Driver
3. 核心实现与动态切换
3.1 注解驱动开发模式
在Service层方法上使用@DS注解实现动态切换:
java复制@Service
public class UserServiceImpl implements UserService {
@DS("master") // 默认数据源
public User getMasterUser(Long userId) {
return userMapper.selectById(userId);
}
@DS("slave1") // 切换至从库
public User getSlaveUser(Long userId) {
return userMapper.selectById(userId);
}
}
3.2 事务处理的特殊要求
多数据源环境下事务管理需要特别注意:
java复制@DS("slave1")
@Transactional(rollbackFor = Exception.class)
public void updateWithTransaction(User user) {
// 该方法内所有数据库操作都会在slave1数据源上执行
userMapper.updateById(user);
logMapper.insert(user.getLog());
}
重要提示:跨数据源事务需要通过分布式事务解决方案(如Seata)实现。我在金融项目中就曾踩过这个坑,两个数据源的本地事务提交后,出现了一个成功一个失败的情况。
4. 高级配置与性能优化
4.1 连接池调优建议
针对不同业务场景配置独立连接池:
yaml复制slave1:
url: jdbc:mysql://192.168.1.100:3306/ry-report
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
4.2 多数据源监控方案
集成Druid监控多个数据源:
java复制@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean<StatViewServlet> druidServlet() {
ServletRegistrationBean<StatViewServlet> servlet = new ServletRegistrationBean<>();
servlet.setServlet(new StatViewServlet());
servlet.addUrlMappings("/druid/*");
return servlet;
}
}
5. 实战问题排查指南
5.1 典型异常处理
-
数据源找不到异常:
log复制Could not find datasource: slave2检查点:
- yml配置缩进是否正确
- 数据源名称是否包含特殊字符
- 是否开启了strict模式但未配置默认数据源
-
事务失效问题:
log复制Transaction synchronization is not active解决方案:
- 确保@Transactional和@DS注解同时加在类或方法上
- 检查是否在同一个类中自调用
5.2 性能优化记录
在压力测试中发现从库查询延迟高,通过以下调整解决:
- 为报表库单独配置了连接池参数
- 添加了@DS注解的缓存机制
- 对高频查询实现了本地缓存
6. 扩展应用场景
6.1 多租户实现方案
结合数据源切换实现SaaS多租户:
java复制public class TenantContext {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setTenant(String tenant) {
CONTEXT.set(tenant);
}
public static String getTenant() {
return CONTEXT.get();
}
}
@Aspect
@Component
public class TenantAspect {
@Before("execution(* com..service.*.*(..))")
public void before() {
String tenant = TenantContext.getTenant();
DynamicDataSourceContextHolder.push(tenant + "_ds");
}
}
6.2 读写分离实战
通过AOP自动路由读写操作:
java复制@Aspect
@Component
public class ReadWriteAspect {
@Before("@annotation(org.springframework.transaction.annotation.Transactional)")
public void setWriteDataSource() {
DynamicDataSourceContextHolder.push("master");
}
@Before("execution(* com..service.*.get*(..)) || execution(* com..service.*.select*(..))")
public void setReadDataSource() {
DynamicDataSourceContextHolder.push("slave1");
}
}
经过多个项目的实践验证,这套多数据源配置方案在日均百万级请求量的系统中表现稳定。特别是在最近一次大促活动中,通过动态调整数据源策略,成功应对了瞬时十倍流量增长。