Shardingsphere,Mybatis Plus之使用自定义精确分片算法---PreciseShardingAlgorithm

前言

在前面进行了基本的Shardingsphere之后,在一些其他的复杂条件下,可以使用自定义精确分片算法(PreciseShardingAlgorithm),通常用来处理=或者in条件的情况比较多。在该demo中,通过user_id来分库,公司Id(company_id)来分表,实现精确的不同分库分表。

数据库表

CREATE TABLE `b_order0` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `is_del` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否被删除',
  `company_id` int NOT NULL COMMENT '公司ID',
  `position_id` bigint NOT NULL COMMENT '职位ID',
  `user_id` int NOT NULL COMMENT '用户id',
  `publish_user_id` int NOT NULL COMMENT '职位发布者id',
  `resume_type` int NOT NULL DEFAULT '0' COMMENT '简历类型:0 附件 1 在线',
  `status` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '投递状态 投递状态 WAIT-待处理 AUTO_FILTER-自动过滤 PREPARE_CONTACT-待沟通 REFUSE-拒绝 ARRANGE_INTERVIEW-通知面试',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '操作时间',
  `work_year` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '工作年限',
  `name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '投递简历人名字',
  `position_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '职位名称',
  `resume_id` int DEFAULT NULL COMMENT '投递的简历id(在线和附件都记录,通过resumeType进行区别在线还是附件)',
  PRIMARY KEY (`id`),
  KEY `i_comId_pub_ctime` (`company_id`,`publish_user_id`,`create_time`) USING BTREE,
  KEY `index_companyId_positionId` (`company_id`,`position_id`) USING BTREE,
  KEY `index_companyId_status` (`company_id`,`status`(255),`is_del`) USING BTREE,
  KEY `index_createTime` (`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=983779921828511746 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

数据源和表截图:2个数据源,4个表
在这里插入图片描述

其他的配置

其他配置见该栏前一篇文章:配置信息

yml(application-custom-sharding.yml)配置:

spring:
  sharding-sphere:
    datasource:
      names: ftdb0,ftdb1
      ftdb0:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/ftdb0?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123mysql
      ftdb1:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/ftdb1?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123mysql
    sharding:
      tables:
        b_order:
          actualDataNodes: ftdb${0..1}.b_order${0..1}
          database-strategy:  # 数据库精确精确分库策略
            standard: #这个地方不可忽略
              sharding-column: user_id
              precise-algorithm-class-name: com.example.ftserver.sharding.shardingalgrithm.precise.FtDataBasePreciseShardingAlgorithm
          key-generator:
            column: id
            type: SNOWFLAKE
          tableStrategy:  #表精确分片策略
            standard:
              shardingColumn: company_id
              preciseAlgorithmClassName: com.example.ftserver.sharding.shardingalgrithm.precise.FtTablePreciseShardingAlgorithm

主配置文件application.yml需要引入自定义的分片配置:
在这里插入图片描述

简单的精确算法示例

在该demo中, 需要根据用户Id(user_id%2)取模来分库,尾数为奇数的去到数据库:ftdb1,尾数为偶数的去数据库:ftdb0;且还需要根据公司Id(company_id%2)取模,尾数为奇数的去到b_order1,尾数为偶数的去b_order0。其他的自定义算法可以根据自身实际情况重新编写
数据库分片算法示例

package com.example.ftserver.sharding.shardingalgrithm.precise;

import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * @author:  zyh
 * @description:  自定义精确分片算法测试:数据库分片,根据userId精确分片
 *
 */
@Component
@Slf4j
public class FtDataBasePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> {
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) {
        // 获取分片键的值
        Integer userId = preciseShardingValue.getValue();
        String logicTableName = preciseShardingValue.getLogicTableName();
        log.info("分片的userId: {},逻辑表名 logicTableName: {}",userId,logicTableName);
        // 根据userId对数据库进行了分片,这里可以根据实际情况进行修改
        String dataSourceName = "ftds" + (userId % 2); // 我们有两个数据源,这里取模得到分片结果
        log.info("分片结果 dataSourceName: {}",dataSourceName);
        // 返回对应的数据源名称
        for (String name : collection) {
            if (name.endsWith(String.valueOf(userId % 2))) {
                return name;
            }
        }
        return dataSourceName;

    }
}

精确分表示例代码:

package com.example.ftserver.sharding.shardingalgrithm.precise;

import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Objects;

/**
 * &#064;author:  zyh
 * &#064;description:  自定义精确分片算法测试:表分片,根据company_id精确分片
 *
 */
@Component
@Slf4j
public class FtTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
        // 获取分片的公司Id
        long companyId = 0L;
        if (Objects.nonNull(preciseShardingValue.getValue())) {
            if (preciseShardingValue.getValue() instanceof  Long) {
                companyId = preciseShardingValue.getValue();
            } else {
                // 不知道为何返回的是Integer
                // 这里需要转换成Long,否则会有异常
                String value = String.valueOf(preciseShardingValue.getValue());
                companyId=Long.parseLong(value);
            }

        }
        // 模拟根据分片键的值取模,可以使用其他算法
        long result = companyId % 2;
        // 获取逻辑表名
        String logicTableName = preciseShardingValue.getLogicTableName();
        // 实际表名
        String physicalTableName = logicTableName + result;
        log.info("分片键的值:{},物理表名:{}", companyId, physicalTableName);
        if (collection.contains(physicalTableName)) {
            return  physicalTableName;
        }

     throw new UnsupportedOperationException("未找到对应的分表信息,表:"+logicTableName+",分片键的值:"+companyId);
    }
}

测试代码及结果:

package com.example.ftserver;

import cn.hutool.core.util.RandomUtil;
import com.example.ftserver.mapper.BOrderMapper;
import com.test.ft.common.entity.BOrderEntity;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Repeat;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest()
public class CustomPreciseShardingTest {
    @Autowired
    private BOrderMapper mapper;

    @Test
    @Repeat(4) // SpringBoot提供的重复执行注解,该配置相当于重复执行4次
    public  void  testCustomPreciseSharding(){
        BOrderEntity entity = new BOrderEntity();
        entity.setIsDel(false);
        // Hutool提供的随机数工具类
        entity.setCompanyId(RandomUtil.randomInt(100,200)); 
        entity.setPositionId(RandomUtil.randomLong(5,10));
        entity.setUserId(RandomUtil.randomInt(2,5));
        entity.setPublishUserId(101);
        entity.setResumeType(1);
        entity.setStatus("Auto");
        entity.setWorkYear(String.valueOf(RandomUtil.randomInt(2)));
        entity.setName("数据库运维工程师");
        entity.setPositionName("数据库运维工程师");
        entity.setResumeId(RandomUtil.randomInt(5));
        int insert = mapper.insert(entity);
        System.out.println("insert = "+ insert);
    }
    

}

执行结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到对应的数据已经根据设置的分片插入了不同的数据库
数据库数据:
在这里插入图片描述

精确分片查询注意事项:

1.根据Id查询时
在这里插入图片描述
可以看到,在每个库,每个表里面都做了一次查询,这是因为b_order表的Id没有作为分片键,走了全路由查询
在这里插入图片描述
2.使用库或者表分片键作为精确查询条件时:
使用表分片键查询:
在这里插入图片描述
在这里插入图片描述
使用库分片键查询时:
在这里插入图片描述
可以看到在使用库或者表分片键作为精确查询条件,只会执行该分片键相对应的查询路径

3.使用分库和分表的分片键查询:
在这里插入图片描述
可以看到SQL查询,走了全部的分库分表的路径查询
结论: 由此测试可以得到结论,在执行了精确分库分表算法之后,在查询之前,最好根据对应的分库分表算法,对查询的参数根据算法预处理和封装,以实现最佳的查询效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值