【经验分享】ShardingSphere+Springboot-04:自定义分片算法(COMPLEX/STANDARD)

3.4 CLASS_BASED 自定义类分片算法

3.4.1 复杂分片自定义算法(strategy=COMPLEX )

通过配置分片策略类型和算法类名,实现自定义扩展。 掌握自定义类算法就算法完全掌握了分片算法的精髓,可以根据业务需求灵活配置分片规则。

类型:CLASS_BASED

可配置属性:

属性名称数据类型说明
strategyString分片策略类型,支持 STANDARD、COMPLEX 或 HINT(不区分大小写)
algorithmClassNameString分片算法全限定名

官方文档给出了一个参考案例如下,没有太多的说明,这尝试用自定义分片算法重新实现[3.3.1]中的行表达式分表规则

package org.apache.shardingsphere.sharding.fixture;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.util.Collection;

public final class ClassBasedStandardShardingAlgorithmFixture implements StandardShardingAlgorithm<Integer> {

@Override
public String doSharding(final Collection<String> availableTargetNames, final PreciseShardingValue<Integer> shardingValue) {
  for (String each : availableTargetNames) {
      if (each.endsWith(String.valueOf(shardingValue.getValue() % 4))) {
          return each;
      }
  }
  return null;
}

@Override
public Collection<String> doSharding(final Collection<String> availableTargetNames, final RangeShardingValue<Integer> shardingValue) {
  return availableTargetNames;
}
}

实现目标:根据用户类型user_type和部门dep_id进行复杂分库分表

编写分片算法类:

  1. 根据分片策略类型( STANDARD、COMPLEX 或 HINT)实现对应的算法接口,这里需要用到的复杂算法的接口

    image-20240809111027491

  2. 实现必须的方法,这里核心关注doSharding方法,编写思路记录如下

    • 获取列名和分片值的映射(必备的,获取了才能根据分片键值计算数据源嘛
    • (可选)获取逻辑表名、范围值映射
    • 获取分片键值,并计算得到真实数据源

完整算法类:

package com.zhc.shard.algo;

import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;

import java.util.*;

/**
 * @Author zhuhuacong
 * @Date: 2024/08/09/ 11:07
 * @description
 */
@Slf4j
public class UserTypeDepIdAlgorithm implements ComplexKeysShardingAlgorithm<String> {
    // 定义列名常量
    private static final String col1 = "user_type";
    private static final String col2 = "dep_id";

    /**
     * 实现分片逻辑
     * 当分片条件存在时,根据给定的分片键计算出特定的数据源
     * 如果无法计算出合适的分片结果,则抛出异常
     *
     * @param collection               数据源集合(这里是分表策略,所以这里是真实表集合
     * @param complexKeysShardingValue 分片条件对象
     * @return 分片后的数据源名称列表
     * @throws RuntimeException 当无法得到合适的分片结果时
     */
    @Override
    public Collection<String> doSharding(Collection collection, ComplexKeysShardingValue complexKeysShardingValue) {
        // 获取逻辑表名
        String logicTableName = complexKeysShardingValue.getLogicTableName();

        // 获取列名和分片值的映射
        Map columnNameAndShardingValuesMap = complexKeysShardingValue.getColumnNameAndShardingValuesMap();

        // 获取列名和范围值的映射(未用到)
        Map columnNameAndRangeValuesMap = complexKeysShardingValue.getColumnNameAndRangeValuesMap();

        // 获取分片键值
        List c1UserType = (List) columnNameAndShardingValuesMap.get(col1);
        List c2DepId = (List) columnNameAndShardingValuesMap.get(col2);

        // 如果任一分片键存在,则进行分片计算
        if (Optional.ofNullable(c1UserType).isPresent() || Optional.ofNullable(c2DepId).isPresent()) {
            // 【算法核心】 根据分片键值计算数据源索引
            long userType = c1UserType.get(0) != null ? Long.parseLong(c1UserType.get(0).toString()) : 0;
            long depId = c2DepId.get(0) != null ? Long.parseLong(c2DepId.get(0).toString()) : 0;
            long shardingId = ((userType + depId) % 2);
            String tableName = logicTableName + "_" + shardingId;
            log.info("userType:{};shardingId:{};余数{};真是表名{}", userType, depId, shardingId , tableName);

            // 返回计算后的数据源名称
            return Collections.singletonList(tableName);
        }
        // 如果无法得到合适的分片结果,则记录日志并抛出异常
        log.info("没有得到合适的分片结果=:表名{},参数:{}", collection, columnNameAndShardingValuesMap);
        throw new RuntimeException("没有得到合适的分片结果");
    }


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

    @Override
    public void init(Properties properties) {

    }
}

进行测试

分表策略运行正确

image-20240809114449222

3.4.2 STANDARD 标准分片自定义算法

快速实现,表结构还是参考前面的打卡表。要求根据早中晚三个时段的打卡时间进行分表,分别存入

stu_clock_am、stu_clock_noon、stu_clock_eve;

yaml配置如下

spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          # 打卡表
          stu_clock:
            actual-data-nodes: dsmain.stu_clock_am,dsmain.stu_clock_noon,dsmain.stu_clock_eve
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
            table-strategy:
              standard:
                sharding-column: clockin_time
                sharding-algorithm-name: stu_clock_class_algo
        # 配置分片算法
        sharding-algorithms:
          stu_clock_class_algo:
            type: CLASS_BASED
            props:
              #分片策略类型,支持 STANDARD、COMPLEX 或 HINT(不区分大小写)
              strategy: STANDARD
              # 分片算法全限定名
              algorithmClassName: com.zhc.shard.algo.StuClockClassAlgorithm

算法代码

package com.zhc.shard.algo;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;

/**
 * @Author zhuhuacong
 * @Date: 2024/08/09/ 14:56
 * @description StuClockClassAlgorithm
 */
@Slf4j
public class StuClockClassAlgorithm implements StandardShardingAlgorithm<Date> {


    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
        String logicTableName = shardingValue.getLogicTableName();
        Date date = shardingValue.getValue();
        LocalDateTime localDateTime = DateUtil.toLocalDateTime(date);
        int hour = localDateTime.getHour();
        log.info("logicTableName: {} value: {} hour {}", logicTableName, date, hour);
        String actualTableName =null;
        if (hour > 15){
            actualTableName =  logicTableName + "_eve";
        } else if (hour > 10 ){
            actualTableName =  logicTableName + "_noon";
        } else {
            actualTableName =  logicTableName + "_am";
        }
        if (availableTargetNames.contains(actualTableName)){
            return actualTableName;
        }
        // 如果无法得到合适的分片结果,则记录日志并抛出异常
        log.info("没有得到合适的分片结果:表名{},参数:{}", shardingValue, shardingValue);
        throw new RuntimeException("没有得到合适的分片结果");
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> shardingValue) {
        return availableTargetNames;
    }

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

    @Override
    public void init(Properties properties) {

    }
}

结果测试,

生成随机打卡时间,观察是否正确入库

image-20240809153044358

结果符合预期

## 进阶⭐️ 自定义算法+范围查询优化

可以发现,StandardShardingAlgorithm接口还有一个同样叫doSharding的接口,

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or table names
     * @param shardingValue sharding value
     * @return sharding results for data sources or table names
     */
    Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<T> shardingValue);

传入的参数是一个带有范围(上下界限)shardingValue,一个是可用的真实表集合availableTargetNames,返回参数就是需要查询的目标表列表。

只要根据实际业务逻辑实现方法体内容即可。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
自定义分片算法,可以按照以下步骤操作: 1. 实现ShardingAlgorithm接口,该接口有一个方法doSharding,用于根据分片键和分片策略计算出目标数据节点。 ```java public interface ShardingAlgorithm<T extends Comparable<?>> { /** * 计算分片结果. * * @param availableTargetNames 所有的可用数据节点名称集合, 一般情况下与数据源名称相同 * @param shardingValue 分片值 * @return 分片后指向的数据节点名称,即逻辑数据源名称 */ String doSharding(Collection<String> availableTargetNames, ShardingValue<T> shardingValue); } ``` 其中,ShardingValue是分片值对象,包含分片键的值和分片算法的类型。availableTargetNames是所有可用的数据节点名称集合,一般情况下与数据源名称相同。 2. 使用自定义分片算法 在使用shardingsphere-sharding-boot-starter时,可以在application.yml中配置分片规则和自定义分片算法。假设要对user表按照年龄进行分片,并使用自定义分片算法,可以按照以下配置: ```yaml spring: shardingsphere: datasource: names: ds0, ds1 ds0: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 username: root password: root ds1: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/test1?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 username: root password: root sharding: tables: user: actual-data-nodes: ds$->{0..1}.user_$->{0..1} table-strategy: standard: sharding-column: age precise-algorithm-class-name: com.example.MyPreciseShardingAlgorithm range-algorithm-class-name: com.example.MyRangeShardingAlgorithm key-generator: column: id type: SNOWFLAKE ``` 其中,MyPreciseShardingAlgorithm和MyRangeShardingAlgorithm分别是自定义的精确分片算法和范围分片算法,可以在代码中实现。 需要注意的是,自定义分片算法必须实现ShardingAlgorithm接口,并在application.yml中配置算法类的全限定名。在配置中,可以通过sharding-column指定分片键,通过actual-data-nodes指定真实的数据节点。具体的配置可以根据实际情况进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Xcong_Zhu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值