shardingJdbc5实现分库分表,多租户

本文通过一个实战案例,演示了如何在SpringBoot项目中结合Mybatis-Plus和ShardingJdbc5实现数据库的分库分表。详细步骤包括:初始化环境,引入ShardingJdbc依赖,配置YML文件,自定义分库分表规则,实现Hint分库策略以支持多租户。同时,文章还提到了配置过程中的注意事项和常见问题,如默认策略的使用,以及自定义策略不生效的原因等。

实现案例springboot+mybatis-plus+shardingJdbc5

1.准备工作,springboot+mybatis-plus工程,创建两个数据库,各包含t_pro_area,t_pro_fram两张表,字段随意

2.引入shardingjdbc  jar

<!-- 分库分表  -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.1.2</version>
</dependency>

3.yml文件配置

spring:
  shardingsphere:
    props:
      # 日志显示具体的SQL
      sql-show: false
    # 是否开启
    datasource:
      # 数据源(逻辑名字)
      names: ds0,ds1
      # 配置数据源
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: jdbc:mysql://localhost:3306/fishery-1?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimeZone=GMT%2B8&allowPublicKeyRetrieval=true
        username: root
        password: 123456
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: jdbc:mysql://localhost:3306/fishery-2?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimeZone=GMT%2B8&allowPublicKeyRetrieval=true
        username: root
        password: 123456

    # 分片的配置
    rules:
      sharding:
        # 表的分片策略
        tables:
          # 逻辑表的名称
          t_pro_fram:
            # 数据节点配置,采用Groovy表达式
            actual-data-nodes: ds$->{0..1}.t_pro_fram
            # 配置策略
            database-strategy: #分库策略
              standard:
                sharding-column: sort   # 分片列名称
                sharding-algorithm-name: user_inline # 分片算法名称

#            table-strategy: #分表策略
#              standard:
#                sharding-column: sort
#                sharding-algorithm-name: database_alg
        
        #分片算法名
        sharding-algorithms:
          user_inline:
            type: inline
            props:
              algorithm-expression: ds$->{sort % 2}

测试controller

@RestController
@RequestMapping("test")
public class tController {  

    @Autowired
    private ProFarmService proFarmService;

    @GetMapping("/list")
    public String list() {


        for (int i = 1 ; i < 10; i++) {
            ProFarm test =new ProFarm();
            test.setCusNo("嗡嗡嗡"+i);
            test.setSort(i);
            proFarmService.save(test);
        }
        retun "seccess";
    }
}

规则ds$->{sort % 2},实现按表字段sort取余,分配到ds0或ds1数据库中。分表同理,与否相同不做演示。

4.自定义分库规则实现StandardShardingAlgorithm接口

public class DatabaseAlgorithm implements StandardShardingAlgorithm<String> {
    @Override
    public String doSharding(Collection<String> collection,         PreciseShardingValue<String> preciseShardingValue) {
        System.out.println("123");

        for(String aa : collection){ //所有库或表
            System.out.println(aa);
            //执行逻辑,返回需要执行的库或表
        }

        //返回需要具体操作的库或表名
        return null;
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
        System.out.println("123+");
        return null;
    }

    @Override
    public Properties getProps() {
        System.out.println("123+getProps");
        return null;
    }

    @Override
    public void init(Properties properties) {
        System.out.println("123+init");
    }
}

yml修改

    # 分片的配置
    rules:
      sharding:
        # 表的分片策略
        tables:
          # 逻辑表的名称
          t_pro_fram:
            # 数据节点配置,采用Groovy表达式
            actual-data-nodes: ds$->{0..1}.t_pro_fram
            # 配置策略
            database-strategy: #分库策略
              standard:
                sharding-column: sort   # 分片列名称
                sharding-algorithm-name: database_alg # 分片算法名称

#            table-strategy: #分表策略
#              standard:
#                sharding-column: sort
#                sharding-algorithm-name: database_alg
        
        #分片算法名
        sharding-algorithms:
          user_inline:
            type: inline
            props:
              algorithm-expression: ds$->{sort % 2}

          database_alg:
            type: CLASS_BASED #类引用
            props:
              strategy: STANDARD
              algorithmClassName: com.iot.common.DatabaseAlgorithm #实现接口的全定义路径

添加了新的算法定义,database_alg

5.自定义Hint分库分表算法

如果需要用额外数据进行分库分表判断,没有用到数据表字段,比如实现多租户,需要取当前登录标识。

实现HintShardingAlgorithm接口

     

public class DatabaseHintAlgorithm implements HintShardingAlgorithm<String> {


    @Override
    public Collection<String> doSharding(Collection<String> collection, HintShardingValue<String> hintShardingValue) {

        List<String> shardingResult = new ArrayList<>();
        String bb ="ds0";
        for(String aa : collection){
            System.out.println(aa);

        }

        shardingResult.add(bb); //size只能为1,返回需要操作的表或库
        System.out.println(bb);
        return shardingResult;
    }

    @Override
    public Properties getProps() {
        return null;
    }

    @Override
    public void init(Properties properties) {
        System.out.println("DatabaseHintAlgorithm-----init");
    }
}

yml

    # 分片的配置
    rules:
      sharding:
        # 表的分片策略
        tables:
          # 逻辑表的名称
          t_pro_fram:
            # 数据节点配置,采用Groovy表达式
            actual-data-nodes: ds$->{0..1}.t_pro_fram
            # 配置策略
            database-strategy: #分库策略
              hint:
                sharding-algorithm-name: hint_alg

#            table-strategy: #分表策略
#              standard:
#                sharding-column: sort
#                sharding-algorithm-name: database_alg
        
        #分片算法名
        sharding-algorithms:
          user_inline:
            type: inline
            props:
              algorithm-expression: ds$->{sort % 2}

          database_alg:
            type: CLASS_BASED #类引用
            props:
              strategy: STANDARD
              algorithmClassName: com.iot.common.shardingJdbc.DatabaseAlgorithm #实现接口的全定义路径

          hint_alg:
            type: CLASS_BASED
            props:
              strategy: HINT
              algorithmClassName: com.iot.common.shardingJdbc.DatabaseHintAlgorithm

添加了hint_alg 算法,此时还没完成,现在运行发现不进入hint的自定义算法中,需要添加HintManager 操作,

addDatabaseShardingValue("t_pro_fram","1");表示进行t_pro_fram表相关操作时,在把1传值到自定义的算法的参数hintShardingValue,addTableShardingValue("","")同理,传到表算法定义的参数上,没有指定会爆错。

不想一个个表添加,使用hintManager.setDatabaseShardingValue("ds1");所有定义算法的表都传ds1.

多租户实现思路:可创建Aop拦截,在所有方法执行前,执行相关逻辑,把标识放入hintManager,方法后统一执行close();
@RestController
@RequestMapping("test")
public class tController {  

    @Autowired
    private ProFarmService proFarmService;

    @GetMapping("/list")
    public String list() {
        
        // Hint分片策略必须要使用 HintManager工具类
        HintManager hintManager = HintManager.getInstance();
        hintManager.addDatabaseShardingValue("t_pro_fram","1");
        // 直接指定对应具体的数据库
        //hintManager.setDatabaseShardingValue("ds1");

        for (int i = 1 ; i < 10; i++) {
            ProFarm test =new ProFarm();
            test.setCusNo("嗡嗡嗡"+i);
            test.setSort(i);
            proFarmService.save(test);
        }

        hintManager.close();

        retun "seccess";
    }
}

6.默认分库策略default-database-strategy

一般我们很多表执行分库算法,不想一个个添加,发现官网有配置默认分库策略

误区:没有配置分库分表策略的会走default-database-strategy?

答:不会,具体ShardingShpere配置default-database-strategy或default-table-strategy失效原因_Rico_Yip的博客-CSDN博客_table-strategy

5版本没有了默认数据库配置,如果没有配置规则,会默认操作第一个数据源,如ds0

默认策略正确使用如下,配置了t_pro_area,但没有具体实现策略,就是走默认策略

    # 分片的配置
    rules:
      sharding:
        # 表的分片策略
        tables:
          # 逻辑表的名称
          t_pro_fram:
            # 数据节点配置,采用Groovy表达式
            actual-data-nodes: ds$->{0..1}.t_pro_fram
            # 配置策略
            database-strategy: #分库策略
              hint:
                sharding-algorithm-name: hint_alg

#            table-strategy: #分表策略
#              standard:
#                sharding-column: sort
#                sharding-algorithm-name: database_alg

          t_pro_area:
            actual-data-nodes: ds$->{0..1}.t_pro_farm

        default-database-strategy: #默认分库策略
          hint:
            sharding-algorithm-name: hint_alg
        default-table-strategy: #默认分表策略
          none: #没有
        
        #分片算法名
        sharding-algorithms:
          user_inline:
            type: inline
            props:
              algorithm-expression: ds$->{sort % 2}

          database_alg:
            type: CLASS_BASED #类引用
            props:
              strategy: STANDARD
              algorithmClassName: com.iot.common.shardingJdbc.DatabaseAlgorithm #实现接口的全定义路径

          hint_alg:
            type: CLASS_BASED
            props:
              strategy: HINT
              algorithmClassName: com.iot.common.shardingJdbc.DatabaseHintAlgorithm

AOP统一实现


@Aspect
@Order(-10)
@Component
public class DyanicDataSourceAscept {

    //自己配置的读取配置文件,循环所参与策略的表,根据自己实际自定义配置字段
    @Autowired
    private ShardingJdbcProperties shardingJdbcProperties;
        

    /**
     * 定义一个方法,用于声明切入点表达式,方法中一般不需要添加其他代码 使用@Pointcut声明切入点表达式 后面的通知直接使用方法名来引用当前的切点表达式;如果是其他类使用,加上包名即可
     */
    @Pointcut("execution(public * com.*.*.controller.*Controller.*(..)) && !execution(public * com.*.admin.controller.AuthController.*(..))")
    public void declearJoinPointExpression() {
    }

    /**
     * 前置通知
     *
     * @param joinPoint
     */
    public void beforMethod(JoinPoint joinPoint) {
        
        //修改为你自己的用户标识
        LoginUser loginUser = UserUtils.getLoginUser();
        System.out.println("ThreadLocal - set beforMethod:"+loginUser.getTenantNo());

        //取余数作为表后缀,从1开始
        int tableIndex = loginUser.getTenantNo() % shardingJdbcProperties.getTableCount()+1;

        //取整作为数据库后缀,从0开始
        int dataBaseIndex = loginUser.getTenantNo() / shardingJdbcProperties.getTableCount();

        // Hint分片策略必须要使用 HintManager工具类
        HintManager hintManager = HintManager.getInstance();
        for (String tableName: shardingJdbcProperties.getRoleTables()){
            hintManager.addDatabaseShardingValue(tableName,shardingJdbcProperties.getDataBastPrefix()+dataBaseIndex);
            hintManager.addTableShardingValue(tableName,shardingJdbcProperties.getTableInfix()+tableIndex);
        }
    }

    /**
     * 后置通知(无论方法是否发生异常都会执行,所以访问不到方法的返回值)
     *
     * @param joinPoint
     */
    @After("declearJoinPointExpression()")
    public void afterMethod(JoinPoint joinPoint) {
        System.out.println("After---------------");
        HintManager.clear();
    }

    /**
     * 返回通知(在方法正常结束执行的代码) 返回通知可以访问到方法的返回值!
     *
     * @param joinPoint
     */
    public void afterReturnMethod(JoinPoint joinPoint, Object result) {
        /

    }

    /**
     * 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数) 环绕通知包含前置、后置、返回、异常通知;ProceedingJoinPoin 类型的参数可以决定是否执行目标方法 且环绕通知必须有返回值,返回值即目标方法的返回值
     *
     * @param joinPoint
     */
    @Around(value = "declearJoinPointExpression()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        Object result = null;
        // 前置通知
        beforMethod(joinPoint);

        // 执行目标方法
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        // 后置通知 - 新建
        afterReturnMethod(joinPoint, result);
        return result;
    }

}

注意事项:

        1.如果规则添加了某个表,策略配置错误并且没有配置默认策略,插入的效果会是全库全表,所有库的这张表都插入了数据。

        2.没有定义策略的表默认使用第一个数据源

        3.如果发现自定义的策略不进入,检查yml的格式,建议先配置简单inil类型,实现后确定了yml格式无误后,切换自定义策略(我就是因为格式问题耗费了好多时间,不报错)

        4.如果分库分表都使用hint算法,那么使用addDatabaseShardingValue和addTableShardingValue,一个个表加上去。不能用setDatabaseShardingValue会把TableShardingValue.clen(),导致不进入分表算法。

        5.官方文档还有很多其他内置策略,功能可以研究。概览 :: ShardingSphere

        

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值