Dynamic DataSource 运行时动态添加数据源及切换数据源

984 阅读2分钟

背景

之前工作中有个需求,需要对接第三方的财务系统,他们提供一个前置库,我们定时把数据推送至前置库,于是就想到用baomidou的dynamic datasource,它可以配置连接多个数据源,同时在使用数据源的时候通过@DS随意切换一个数据源,因为我们系统是多租户的,刚开始对接第一家租户时没什么问题,慢慢的,有多家机构需要对接同样的财务系统,而且财务系统是每个租户一套前置库,租户与租户之间网络是不互通的,于是就想着把数据库的配置改成租户参数,根据参数动态的创建数据源,后面新的租户对接只需页面添加数据库配置即可

实现步骤

  1. 在主流程开始时,往dynamicRoutingDataSource 添加数据源
           //使用ThreadLocal保存当前租户标识
            ThreadLocalOrg.set(syncParam.getOrgId());
            //创建druid数据源
            dataSource = new DruidDataSource();
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            dataSource.setUrl(paramMap.get(ParamsConstants.FMS_DB_URL).getVal().trim());
            dataSource.setUsername(paramMap.get(ParamsConstants.FMS_DB_USER).getVal().trim());
            dataSource.setPassword(paramMap.get(ParamsConstants.FMS_DB_PASSWORD).getVal().trim());
            dataSource.setBreakAfterAcquireFailure(true);//失败不重试
            //往DynamicRoutingDataSource添加数据源,key为租户标识
            dynamicRoutingDataSource.addDataSource(syncParam.getOrgId(),dataSource);
  1. 自定义切换数据源注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DS_ORG {
}
  1. 添加切面,实现根据租户id动态切换数据源
@Aspect
@Component
public class DataSourceOrgAspect {

    @Around("@annotation(net.jqsoft.datasync.anno.DS_ORG)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取租户标识
        String dataSourceKey = ThreadLocalOrg.get();
        DynamicDataSourceContextHolder.push(dataSourceKey);
        try {
            return joinPoint.proceed();
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }
}
  1. 整个同步任务结束后,移除当前租户的数据源,并关闭数据源,释放资源
dynamicRoutingDataSource.removeDataSource(syncParam.getOrgId());
if (Objects.nonNull(dataSource) && !dataSource.isClosed()) {
    dataSource.close();
}

优点

  1. 灵活扩展性:支持多租户系统、数据分片、读写分离等复杂场景,无需重启应用即可动态接入新数据源,提升业务适应性‌
  2. 资源优化: 延迟创建连接池,仅在首次使用时初始化,减少未使用数据源的资源占用
  3. 动态管理能力:支持运行时根据业务需求切换数据源,例如通过注解或代码动态路由,增强系统灵活性‌

缺点

  1. 事务管理风险:动态切换可能导致事务上下文不一致,尤其跨数据源操作时易引发事务失效或数据不一致,需额外处理分布式事务‌
  2. 性能开销:频繁创建和销毁连接池会增加系统开销,高并发场景下可能影响响应速度,需合理设计连接池复用策略‌
  3. 监控调试困难:频繁创建和销毁连接池会增加系统开销,高并发场景下可能影响响应速度,需合理设计连接池复用策略‌