1. 项目概述
dynamic-datasource 是一个轻量级的 Java 多数据源管理框架,它能够帮助开发者轻松实现项目中多数据源的动态切换和管理。在实际企业级应用开发中,我们经常需要同时连接多个数据库,可能是不同的业务数据库、读写分离的主从库,或者是分库分表后的多个数据节点。传统的手动管理方式不仅代码冗余,而且容易出错。dynamic-datasource 通过简洁的 API 和注解驱动的方式,让多数据源管理变得简单高效。
我在多个分布式系统中使用过这个框架,它最吸引我的特点是几乎零侵入的集成方式和灵活的动态切换能力。无论是传统的 Spring 项目还是 Spring Boot 应用,都能快速接入。框架内部实现了数据源的路由逻辑,开发者只需要关注业务代码,无需操心连接池的管理细节。
2. 核心设计解析
2.1 架构设计原理
dynamic-datasource 采用了分层设计的思想,核心架构包含以下几个关键组件:
- 数据源容器:维护所有注册的数据源实例,采用 Map 结构存储,Key 为数据源名称
- 路由决策器:根据当前线程上下文决定使用哪个数据源
- 注解解析器:处理 @DS 注解,将其转换为路由指令
- 连接代理:对原生 Connection 进行包装,加入切换和清理逻辑
框架通过 ThreadLocal 保存当前线程的数据源选择,这种设计确保了在多线程环境下也能正确路由。当方法调用进入时,框架会根据注解或编程式指定的数据源名称,从容器中获取对应的数据源实例。
2.2 关键特性实现
2.2.1 动态切换机制
动态切换的核心在于 AbstractRoutingDataSource 的扩展实现。框架重写了 determineCurrentLookupKey 方法,使其能够从以下三个位置按优先级获取数据源名称:
- 方法上的 @DS 注解
- 类上的 @DS 注解
- 通过 DynamicDataSourceContextHolder 手动设置的值
这种多层次的查找策略既保证了灵活性,又提供了合理的默认值机制。在实际使用中,90% 的场景只需要使用注解即可满足需求。
2.2.2 事务管理集成
与 Spring 事务的集成是框架的另一大亮点。通过 DataSourceTransactionManager 的自定义实现,框架确保了:
- 事务方法内部的数据源切换不会影响外层事务
- 同一事务内保持使用同一个数据源连接
- 事务回滚时能够正确关闭连接
这种设计避免了常见的"跨数据源事务"问题,虽然不能实现真正的分布式事务,但保证了基础的事务一致性。
3. 配置与使用详解
3.1 基础配置示例
以下是一个典型的 Spring Boot 配置示例:
yaml复制spring:
datasource:
dynamic:
primary: master # 默认数据源
datasource:
master:
url: jdbc:mysql://localhost:3306/master
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave1:
url: jdbc:mysql://localhost:3306/slave1
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave2:
url: jdbc:mysql://localhost:3306/slave2
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
配置要点说明:
- primary 指定了默认数据源名称
- 每个数据源的配置与标准 Spring 数据源配置完全一致
- 支持所有主流连接池(HikariCP、Druid 等)
3.2 注解使用实践
@DS 注解是框架的核心 API,支持以下使用方式:
java复制// 类级别注解 - 该服务所有方法默认使用slave数据源
@DS("slave")
@Service
public class UserServiceImpl implements UserService {
// 方法级别注解 - 覆盖类级别注解
@DS("master")
public void updateUser(User user) {
// 使用master数据源执行
}
public User getUser(Long id) {
// 使用slave数据源执行
}
}
注解使用的最佳实践:
- 读操作尽量使用从库,写操作必须使用主库
- 避免在同一个类中频繁切换不同数据源
- 对于复杂业务逻辑,考虑使用编程式切换
4. 高级特性与定制
4.1 动态增删数据源
除了静态配置,框架还支持运行时动态管理数据源:
java复制// 添加新数据源
DynamicDataSourceContextHolder.addDataSource("newDS", dataSource);
// 移除数据源
DynamicDataSourceContextHolder.removeDataSource("oldDS");
// 检查数据源是否存在
boolean exists = DynamicDataSourceContextHolder.containsDataSource("testDS");
这个特性在以下场景特别有用:
- 多租户系统中按需加载租户专属数据源
- 数据库扩容后动态添加新节点
- 故障转移时临时切换备用数据源
4.2 自定义路由策略
框架允许通过实现 DataSourceStrategy 接口来自定义路由逻辑:
java复制public class RandomStrategy implements DataSourceStrategy {
@Override
public String determineDataSource(String currentKey,
List<String> slaveKeys) {
// 随机选择一个从库
int index = new Random().nextInt(slaveKeys.size());
return slaveKeys.get(index);
}
}
配置自定义策略:
yaml复制spring:
datasource:
dynamic:
strategy: com.example.RandomStrategy
内置策略包括:
- 轮询(默认)
- 随机
- 第一个
- 最后一个
5. 性能优化与监控
5.1 连接池配置建议
虽然框架支持多种连接池,但根据我的经验,HikariCP 是最佳选择:
yaml复制spring:
datasource:
dynamic:
datasource:
master:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
关键参数说明:
- maximum-pool-size 应根据实际并发量设置
- 生产环境建议关闭 auto-commit
- 合理设置超时时间避免连接泄漏
5.2 监控集成方案
对于使用 Druid 连接池的项目,可以启用监控功能:
java复制@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
return reg;
}
监控指标包括:
- 每个数据源的活跃连接数
- 请求执行时间统计
- SQL 执行频率分析
- 慢查询日志
6. 常见问题排查
6.1 数据源切换失效
可能原因及解决方案:
-
注解未生效:
- 确保 @DS 注解来自 com.baomidou.dynamic.datasource.annotation
- 检查方法是否为 public(Spring AOP 限制)
-
事务传播问题:
- 在 @Transactional 方法内部切换数据源无效
- 解决方案:拆分事务方法或使用编程式切换
-
线程污染:
- 异步任务中未正确清理线程上下文
- 解决方案:使用 DynamicDataSourceContextHolder.clear()
6.2 性能问题分析
典型性能瓶颈及优化:
-
连接泄漏:
- 现象:连接数持续增长不释放
- 解决方案:检查是否正确关闭 Connection,设置合理的超时时间
-
路由开销:
- 现象:简单查询响应慢
- 解决方案:减少不必要的切换,合并同类操作
-
连接池竞争:
- 现象:高并发时大量线程等待连接
- 解决方案:调整连接池大小,考虑读写分离
7. 生产环境实践心得
在实际项目中使用 dynamic-datasource 多年,总结出以下经验:
-
命名规范:
- 主库统一使用"master"命名
- 从库使用"slave_"前缀加序号
- 业务分库使用"biz_"前缀
-
监控告警:
- 对每个数据源设置独立的健康检查
- 监控连接池使用率,超过80%需要扩容
-
故障处理:
- 实现自动故障转移机制
- 准备手动切换脚本应对紧急情况
-
版本升级:
- 小版本可以直接升级
- 大版本升级前在测试环境充分验证
这个框架最让我欣赏的是它的稳定性,在上百个微服务中运行从未出现过严重问题。对于需要简单可靠的多数据源解决方案的项目,dynamic-datasource 绝对是首选。