【项目实战】多租户实现数据库动态切换

背景

最近公司项目中需要做多租户进行数据源切换的业务,目的是要达到租户与租户之间的数据要进行隔离,本次实现的实现是在一台服务器中,去进行多个数据库之间的切换,且不能相互影响。有不明白多租户的相关内容,可以查看资料:
多租户相关内容

多数据源准备工作

参考资料:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611

整体思路

在请求发出的时候就去进行拦截(判断)这个用户所在的数据源,然后再去切换到对应的数据源中。
1、在我们的业务中,用户可以自己注册,然后就会生产一个数据库,那么在新用户进行登录的时候就要去实时的切换数据源。
2、那么对于已有的数据源,在项目启动的时候就去把所有的数据源加载到内存中。

在这里插入图片描述

多数据源切换方式

在数据源切换中,找到有两种切换方式一个自动,另外一个是手动切换。

准备工作

添加依赖:使用最新版本

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
  <version>${version}</version>
</dependency>

自动切换(@DS方式)

配置文件设置

自动切换也就是在配置文件中把需要用到的数据源准备好,这样就可以在使用的时候在注解上进行使用并进行切换。

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master: # 数据源名称
          url: jdbc:mysql://116.204.118.226:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: guyanshuang.
          driver-class-name: com.mysql.cj.jdbc.Driver
# 如下,如果你是确定的几个数据源,可以直接都在yaml配置写死即可
        slave_1:
          url: jdbc:mysql://116.204.118.226:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver

切换数据源代码

@RestController
@RequestMapping("/eee")
public class DataSourceTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private DataSource dataSource;

    private DataSourceCreator dataSourceCreator;
    @DS("master")
    @GetMapping("/selectAll")
    public List selectAll() {
        return  jdbcTemplate.queryForList("select * from test");
    }

    @GetMapping("/selectByCondition")
    @DS("slave_1")
    public List selectByCondition() {
        return  jdbcTemplate.queryForList("select * from test");
    }

这里有两个方法,都使用@DS注解,但是他们的参数是不同的,这里也就是去切换到对应的数据源了。
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。

手动切换

配置文件设置

这里是使用了druid的配置

# 数据源配置
spring:
  datasource:
    druid:
      destroy-method: close
      stat-view-servlet:
        enabled: true
        login-username: admin
        login-password: 123456
    dynamic:
      primary: master
      # /p6spy: true
      lazy: true
      # 配置全局druid参数,请按需配置
      druid:
        initial-size: 5
        max-active: 20
        min-idle: 5
        max-wait: 60000
      datasource:
        master:
          username: root
          password: 123456
          url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          driver-class-name: com.mysql.cj.jdbc.Driver

项目启动加载数据源:使用注解@PostConstruct

@PostConstruct
    public void initDataSource(){
        //查询所有的资源
       List<TanentModel> tanentModels =tantentMapper.queryAllDataSource();

        for (TanentModel tantent:tanentModels) {
            DataSourceProperty dataSourceProperty = new DataSourceProperty();
            dataSourceProperty.setUrl(tantent.getTenantUrl());
            dataSourceProperty.setDriverClassName(tantent.getTenantDriverClassName());
            dataSourceProperty.setUsername(tantent.getTenantUserName());
            dataSourceProperty.setPassword(tantent.getTenantUserPassword());
            dataSourceProperty.setPoolName(tantent.getTenantCode());
            //创建数据源
            javax.sql.DataSource createDataSource = dataSourceCreator.createDataSource(dataSourceProperty);
            //数据源添加到资源池中
            ((DynamicRoutingDataSource)dataSource).addDataSource(dataSourceProperty.getPoolName(),createDataSource);

        }

    }

添加数据源

在这里插入代码片 public void switchDataSource(String phone) {
        TanentModel tanentModel=null;//获取到的数据源
        try {
            if (!StringUtils.isEmpty(phone)){
                tanentModel= tantentMapper.queryDataSource(phone);

                DataSourceProperty dataSourceProperty = new DataSourceProperty();
                dataSourceProperty.setUrl(tanentModel.getTenantUrl());
                dataSourceProperty.setDriverClassName(tanentModel.getTenantDriverClassName());
                dataSourceProperty.setUsername(tanentModel.getTenantUserName());
                dataSourceProperty.setPassword(tanentModel.getTenantUserPassword());
                dataSourceProperty.setPoolName(tanentModel.getTenantCode());
                String poolName=tanentModel.getTenantCode();
                DynamicDataSourceContextHolder.clear();
                //切换到对应poolName的数据源
                // 判断数据源是否存在,不存在则添加
                javax.sql.DataSource createDataSource = dataSourceCreator.createDataSource(dataSourceProperty);
                //根据手机号查到的资源名称,获取资源池中的资源
                DataSource dataSourcePool = ((DynamicRoutingDataSource) this.dataSource).getDataSources().get(poolName);
                //如果资源为空,说明资源池中没有改资源,那么就添加到资源池中
                if (dataSourcePool==null){
                    //动态添加到资源池
                    ((DynamicRoutingDataSource)dataSource).addDataSource(dataSourceProperty.getPoolName(),createDataSource);
                }
                DynamicDataSourceContextHolder.push(poolName);
                String peek = DynamicDataSourceContextHolder.peek();
                System.out.println("当前线程数据源:"+peek);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

添加数据源核心代码:

  DynamicDataSourceContextHolder.push(poolName);

总结

多数据源切换是现代应用开发中的重要功能之一。对于数据源切换,我们可以采取两种方式:@DS切换和手动切换。

@DS切换是一种自动化方式,通过在代码中配置切换规则,系统可以根据预设条件自动切换数据源。这种方式能够减少人工操作的繁琐,提高效率和准确性。它适用于在特定场景下需要频繁切换数据源的情况,例如负载均衡、读写分离等。通过@DS切换,我们可以灵活地管理和调度多个数据源,实现资源的优化利用和负载均衡,从而提升系统的性能和可扩展性。

另一方面,手动切换是一种更加灵活的方式。它允许开发人员根据实际情况进行即时切换,通过编码的方式直接指定使用哪个数据源。这种方式适用于需要根据具体需求灵活切换数据源的场景,例如特定业务逻辑需要使用不同的数据源等。手动切换虽然需要人工介入,但它提供了更大的灵活性和可控性,使开发人员可以根据需要自由切换数据源,满足不同的业务需求。

综上所述,针对多数据源切换,我们可以根据具体业务需求和系统特点选择合适的切换方式。@DS切换适用于频繁切换数据源、需要自动化调度的场景,而手动切换则更加灵活,适用于需要根据实际情况即时切换数据源的情况。通过合理的规划和技术实施,我们可以充分发挥各种切换方式的优势,实现数据源的灵活管理和高效切换,为业务运行提供有力支持。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷艳爽faye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值