ShardingSphere中的ShardingJDBC常见分片算法的实现

news2024/9/27 21:29:16

文章目录

  • ShardingJDBC
    • 快速入门
    • 修改雪花算法和分表策略
    • 核心概念
    • 分片算法
      • 简单INLINE分片算法
      • STANDARD标准分片算法
      • COMPLEX_INLINE复杂分片算法
      • CLASS_BASED自定义分片算法
      • HINT_INLINE强制分片算法
    • 注意事项

ShardingJDBC

Git地址

快速入门

现在我存在两个数据库,并且各自都有两个数据表。我现在先新增一批用户,分别插入两个数据库的多张数据表中

在这里插入图片描述



基本环境搭建

实体类

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
// 注意,我这里给的表名是sys_user 这是一个逻辑表名  而上图中是不存在这个数据表的,
@TableName("sys_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 实体类中的属性最好不用使用id,因为mybatisplus会自动识别id属性,并在新增时使用雪花算法为id属性赋值。
     * 而不会使用ShardingSphere的雪花算法生成,如果一定要使用id属性名,那么就要加上@TableId(value = "uid", type = IdType.AUTO)
     *
     * 也不要在分片键上使用 @TableId(value = "uid", type = IdType.ASSIGN_ID) 这样是使用的mybatisplus的雪花算法生成的key,
     * 也不会使用ShardingSphere的雪花算法生成
     */
    @ApiModelProperty(value = "uid")
//    @TableId(value = "uid", type = IdType.ASSIGN_ID)
    private Long uid;

    @ApiModelProperty(value = "用户名")
    private String username;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "姓名")
    private String name;

    @ApiModelProperty(value = "描述")
    private String description;

    @ApiModelProperty(value = "状态(1:正常 0:停用)")
    private Integer status;

    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @ApiModelProperty(value = "最后修改时间")
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}



Mapper接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
}



maven依赖

<!-- shardingJDBC核心依赖 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.2.1</version>
    <exclusions>
        <exclusion>
            <artifactId>snakeyaml</artifactId>
            <groupId>org.yaml</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 坑爹的版本冲突 -->
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.33</version>
</dependency>

<!-- SpringBoot依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>snakeyaml</artifactId>
            <groupId>org.yaml</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 数据源连接池 -->
<!--注意不要用这个依赖,他会创建数据源,跟上面ShardingJDBC的SpringBoot集成依赖有冲突 -->
<!--        <dependency>-->
<!--            <groupId>com.alibaba</groupId>-->
<!--            <artifactId>druid-spring-boot-starter</artifactId>-->
<!--            <version>1.1.20</version>-->
<!--        </dependency>-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.20</version>
</dependency>



上方 snakeyaml 依赖冲突会报下面的错误

Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.<init>(ShardingSphereYamlConstructor.java:44)

The following method did not exist:

    org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.setCodePointLimit(I)V

The method's class, org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1, is available from the following locations:

    jar:file:/D:/softwareDev/maven/myMaven/repository/org/apache/shardingsphere/shardingsphere-infra-util/5.2.1/shardingsphere-infra-util-5.2.1.jar!/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor$1.class

The class hierarchy was loaded from the following locations:

    null: file:/D:/softwareDev/maven/myMaven/repository/org/apache/shardingsphere/shardingsphere-infra-util/5.2.1/shardingsphere-infra-util-5.2.1.jar
    org.yaml.snakeyaml.LoaderOptions: file:/D:/softwareDev/maven/myMaven/repository/org/yaml/snakeyaml/1.26/snakeyaml-1.26.jar


Action:

Correct the classpath of your application so that it contains a single, compatible version of org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1

在这里插入图片描述



yml配置文件的内容

server:
  port: 8084


spring:
  shardingsphere:
    props:
      # 打印shardingjdbc的日志  shardingsphere5之前的版本配置项是 spring.shardingsphere.props.sql.show,而这里是sql-show
      sql-show: true
    # 数据源配置
    datasource:
      # 虚拟库的名字,并指定对应的真实库
      names: hsdb0,hsdb1
      hsdb0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_sphere1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
        username: root
        password: 1234
      hsdb1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_sphere2?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
        username: root
        password: 1234

    # 分布式序列算法配置
    rules:
      sharding:
        key-generators:
          # 雪花算法,生成Long类型主键。
          # alg_snowflake为我们程序员自定义的字符串名字,可以更改
          alg_snowflake:
            type: SNOWFLAKE  # 建议使用COSID_SNOWFLAKE雪花算法,避免数据分布不均匀
            props:
              worker:
                id: 1
        sharding-algorithms:
          # 指定分库策略 按2取模方式  sys_user_db_alg是我们程序员自定义的字符串名字,可以更改
          sys_user_db_alg:
            type: MOD
            props:
              sharding-count: 2
          # 指定分表策略  INLINE:按单一分片键分表
          sys_user_tbl_alg:
            type: INLINE
            props:
              # 通过uid对2取模 + 1 最终就会是使用sys_user1  sys_user2两张数据表
              algorithm-expression: sys_user$->{uid%2+1}


        # 指定分布式主键生成策略
        tables:
          # sys_user为虚拟表名,可以更改,但需要和实体类中的表名对应上
          sys_user:
            # 指定虚拟表的分片键,以及分片键的生成策略
            key-generate-strategy:
              # 指定分片建为 uid 这个是和数据表中的字段对应的
              column: uid
              # 指定分片键字段的生成策略, alg_snowflake 也就是我们上面自定义的雪花算法
              key-generator-name: alg_snowflake
            # 配置实际分片节点 $->{1..2}表达式的功能就是[1,2]  hsdb0.sys_user1  hsdb0.sys_user2  hsdb1.sys_user1  hsdb1.sys_user2
            actual-data-nodes: hsdb$->{0..1}.sys_user$->{1..2}
            # 配置分库策略,按uid字段取模
            database-strategy:
              standard:
                sharding-column: uid
                sharding-algorithm-name: sys_user_db_alg
            # 指定分表策略  standard-按单一分片键进行精确或范围分片
            table-strategy:
              standard:
                sharding-column: uid
                sharding-algorithm-name: sys_user_tbl_alg



测试类,进行新增10条记录

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestShardingJDBC {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testAdd(){
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setUsername("husahng" + i);
            user.setPassword("123456");
            userMapper.insert(user);
        }
    }
}



日志信息

在这里插入图片描述



插入结果,我们能发现,目前新增的数据被分到了两张数据表中,数据的分布并不平衡。这是因为我们在yml配置文件中指定的分库策略和分表策略导致的

我们的分库策略是根据uid%2 最终决定使用哪一个数据库,分表策略是uid%2+1 。

所以就导致了如果uid为偶数就会存放在hsdb0.sys_user1数据表;如果uid是奇数就会存放在hsdb1.sys_user2数据表




在这里插入图片描述

在这里插入图片描述



修改雪花算法和分表策略

我们接下来就修改一下分表策略

而将sys_user_tbl_alg的计算表达式从sys_user$->{uid%2+1}改成 sys_user$->{((uid+1)%4).intdiv(2)+1} 后,理论上,如果uid是连续递增的,就可以将数据均匀分到四个表里。但是snowflake雪花算法生成的ID并不是连续的,所以有时候还是无法分到四个表。

如下图所示,已经修改了分表策略,但还是只分到hsdb0.sys_user1数据表和hsdb1.sys_user2数据表



在这里插入图片描述
20240803120302397.png&pos_id=img-SabPy9cD-1722676476932)

在这里插入图片描述



所以我们还需要修改一下雪花算法,不使用snowflake雪花算法。关于这一块,后续文档会详细介绍,这里就简单使用mybatisplus的主键通过雪花算法生成

@TableId(value = "uid", type = IdType.ASSIGN_ID)
private Long uid;



或者是使用COSID_SNOWFLAKE雪花算法

在这里插入图片描述



此时数据就平衡的插入到多张数据表中了

在这里插入图片描述



核心概念

  • 虚拟库

    ShardingSphere的核心就是提供一个具备分库分表功能的虚拟库,他是一个ShardingSphereDatasource实例。应用程序只需要像操作单数据源一样访问这个ShardingSphereDatasource即可。

    上方案例中,hsdb1 hsdb2就是虚拟库

  • 真实库

    实际保存数据的数据库。真实库被包含在ShardingSphereDataSource实例中,由ShardingSphere来决定未来使用哪一个真实库

    上方案例中,sharding_sphere1和sharding_sphere2就是真实库

  • 逻辑表

    应用程序直接操作的逻辑表。

    上方案例中,sys_user就是逻辑表

  • 真实表

    真实保存数据的表。逻辑表和真实表的表名不需要一致,但需要有一致的表结构。应用程序可以维护一个真实表与逻辑表的对应关系。

    上方案例中,sys_ser1 sys_user2就是真实表

  • 分布式主键生成算法

    给逻辑表生成唯一的主键。

    由于逻辑表的数据是分布在多个真实表当中的,所以单表的索引就无法保证逻辑表的ID唯一性。ShardingSphere集成了几种常见的基于单机生成的分布式主键生成器。比如SNOWFLAKE,COSID_SNOWFLAKE雪花算法可以生成单调递增的long类型的数字主键,还有UUID,NANOID可以生成字符串类型的主键。当然,ShardingSphere也支持应用自行扩展主键生成算法。比如基于Redis,Zookeeper等第三方服务,自行生成主键。

  • 分片策略

    表示逻辑表要如何分配到真实库和真实表当中,分为分库策略和分表策略两个部分。

    分片策略由分片键和分片算法组成。分片键是进行数据水平拆分的关键字段。如果没有分片键,ShardingSphere将只能进行全路由,SQL执行的性能会非常差。分片算法则表示根据分片键如何寻找对应的真实库和真实表。



分片算法

简单INLINE分片算法

可以对分片键使用 = 或者是 in 进行精确查找。uid通过下面的分片表达式计算出要查询的真实库和真实表。

如果in操作涉及到了多个分片 并且 涉及到了多个数据表,也会进行多分片多数据表的查询

默认情况下不能使用范围查询

我们现在yml的配置的分表策略是

spring:
  shardingsphere:
    rules:
      sharding:
        sharding-algorithms:
          sys_user_tbl_alg:
            # 分片策略是 INLINE
            type: INLINE
            props:
              # 通过(uid+1) %4 /2 +1
              algorithm-expression: sys_user$->{((uid+1)%4).intdiv(2)+1}
// 使用 = 对分片进行精确查询
@Test
public void testInline(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("uid", 1819587788580864004L);
    User user = userMapper.selectOne(queryWrapper);
    System.out.println(user);
}



日志信息,仅仅查询到一个库的一个表进行查询

Logic SQL: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user WHERE (uid = ?)

Actual SQL: hsdb0 ::: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user1 
WHERE (uid = ?) ::: [1819587788580864004]



// in操作 涉及到的都在是一个分片库中
@Test
public void testInline(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.in("uid", 1819587788580864004L,1819587788513755138L);
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}

日志信息,会查询hsdb0分片库中的sys_user1 和 sys_user2 数据表,并通过union关键字进行组合查询

Logic SQL: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user WHERE (uid IN (?,?))

Actual SQL: hsdb0 ::: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user1 
WHERE (uid IN (?,?)) 
UNION 
ALL SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user2 
WHERE (uid IN (?,?)) ::: [1819587788580864004, 1819587788513755138, 1819587788580864004, 1819587788513755138]



// in操作 涉及到两个分片中的sys_user1数据表
@Test
public void testInline(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.in("uid", 1819587788580864004L,1819587788580864003L);
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}



此时我更改一个查询的uid,让它涉及到两个分片中的sys_user1数据表,此时从打印的日志就可以发现这里只查询了两个分片的sys_user1数据表

Logic SQL: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user WHERE (uid IN (?,?))

Actual SQL: hsdb0 ::: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user1 
 WHERE (uid IN (?,?)) ::: [1819587788580864004, 1819587788580864003]
Actual SQL: hsdb1 ::: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user1 
 WHERE (uid IN (?,?)) ::: [1819587788580864004, 1819587788580864003]



// in操作 涉及到两个分片中的两张数据表,主要就是sys_user$->{((uid+1)%4).intdiv(2)+1}这个分片表达式不能很好的进行区分了
@Test
public void testInline(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.in("uid", 1819587788580864004L,1819587784852127745L);
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}

此时我更改一个查询的uid,让它设计到两个分片的两个数据表,此时从打印的日志就可以发现这里进行了两个分片与两个数据表的查询

Logic SQL: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user WHERE (uid IN (?,?))

ctual SQL: hsdb0 ::: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user1 
WHERE (uid IN (?,?)) UNION ALL SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user2 
WHERE (uid IN (?,?)) ::: [1819587788580864004, 1819587784852127745, 1819587788580864004, 1819587784852127745]
 
 Actual SQL: hsdb1 ::: SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user1 
 WHERE (uid IN (?,?)) UNION ALL SELECT  uid,username,password,name,description,status,create_time,update_time  FROM sys_user2 
 WHERE (uid IN (?,?)) ::: [1819587788580864004, 1819587784852127745, 1819587788580864004, 1819587784852127745]



STANDARD标准分片算法

使用简单INLINE分片算法,它默认情况下不支持范围查询。

我们可以通过添加下面这个参数让它支持范围查询,只不过此时的查询是通过全分片路由查询

# 允许在inline策略中使用范围查询。  sys_user_tbl_alg 是自定义名称
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.props.allow-range-query-with-inline-sharding=true



@Test
public void testInline(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.between("uid", 1819587788513755138L, 1819587788580864004L);
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}

报错信息




在这里插入图片描述



# 添加下面的配置 允许在inline策略中使用范围查询。 sys_user_tbl_alg 是自定义名称
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.props.allow-range-query-with-inline-sharding=true

此时的查询就会通过全分片路由查询



在Class_based自定义分片算法中,如果使用标准分片算法则实现下面的接口StandardShardingAlgorithm

public interface StandardShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    String doSharding(Collection<String> var1, PreciseShardingValue<T> var2);

    // 如果是范围查询,那么我们就可以通过使用RangeShardingValue 对象来进行自定义范围查询的分片算法
    Collection<String> doSharding(Collection<String> var1, RangeShardingValue<T> var2);
}



COMPLEX_INLINE复杂分片算法

complex_inline复杂分片策略,它支持多个分片键。

建议所有的分片键是同一个类型



修改yml配置

# 分表策略-COMPLEX:按多个分片键组合分表  sys_user_tbl_alg 为我们自定义字符串 可以修改
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.type=COMPLEX_INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.props.algorithm-expression=sys_user$->{(uid+status+1)%2+1}

#给sys_user表指定分表策略 sys_user是虚拟表名   按uid, status多个分片键进行组合分片  sys_user_tbl_alg要和上面的对应上
spring.shardingsphere.rules.sharding.tables.sys_user.table-strategy.complex.sharding-columns=uid, status
spring.shardingsphere.rules.sharding.tables.sys_user.table-strategy.complex.sharding-algorithm-name=sys_user_tbl_alg

在这里插入图片描述



进行新增

@Test
public void testAdd(){
    for (int i = 0; i < 10; i++) {
        User user = new User();
        user.setUsername("husahng" + i);
        user.setPassword("123456");
        user.setName("name");
        user.setDescription("描述信息");
        // 给status也设置值
        user.setStatus(1);
        userMapper.insert(user);
    }
}



通过两个分片字段进行查询

@Test
public void testComplexInline(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.in("uid", 1026510115563372545L,1026510115626287104L);
    queryWrapper.eq("status", 0);
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}



CLASS_BASED自定义分片算法

我们可以通过自定义分片算法,能够提前判断一些不合理的查询条件。就比如我当前status的值只有0和1,如果查询的时候status为3就根本没必要去查询数据库了。

对于complex_inline复杂的分片算法,用一个简单的分片表达式也有点不太够用了。

我们可以创建一个java类,实现ShardingSphere提供的ComplexKeysShardingAlgorithm接口 自己在java代码中写相应的分片策略

STANDARD-> StandardShardingAlgorithm

COMPLEX->ComplexKeysShardingAlgorithm 这里就拿复杂的分片策略举例

HINT -> HintShardingAlgorithm



package com.hs.sharding.algorithm;

import com.google.common.collect.Range;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import org.apache.shardingsphere.sharding.exception.syntax.UnsupportedShardingOperationException;

import java.util.*;

/**
 * 实现自定义COMPLEX分片策略
 * 声明算法时,ComplexKeysShardingAlgorithm接口可传入一个泛型,这个泛型就是分片键的数据类型。
 * 这个泛型只要实现了Comparable接口即可。
 * 但是官方不建议声明一个明确的泛型出来,建议是在doSharding中再做类型转换。这样是为了防止分片键类型与算法声明的类型不符合。
 * <p>
 * 我此时uid就是long   status是int   我进行测试
 *
 * @auth roykingw
 */
public class MyComplexAlgorithm implements ComplexKeysShardingAlgorithm<Long> {

    private Properties props;

    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Long> complexKeysShardingValue) {
        // 假如此时SQL为 select * from sys_user where uid in (xxx,xxx,xxx) and status between {lowerEndpoint} and {upperEndpoint};
        Collection<Long> uidList = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("uid");
        Range<Long> status = complexKeysShardingValue.getColumnNameAndRangeValuesMap().get("status");

        // 拿到status的查询范围
        if (status != null) {
            Long lowerEndpoint = status.lowerEndpoint();
            Long upperEndpoint = status.upperEndpoint();
            if (lowerEndpoint > upperEndpoint) {
                // 如果下限 > 上限  抛出异常,终止去数据库查询的操作
                throw new UnsupportedShardingOperationException("empty record query", "sys_user");
            } else if (lowerEndpoint < 0 || upperEndpoint > 1) {
                // 如果查询范围明显不在status字段的区间中
                //抛出异常,终止去数据库查询的操作
                throw new UnsupportedShardingOperationException("error range query param", "sys_user");
            }
        }

        // 校验都通过后,就按照自定义的分片规则来进行分片,我这里就按照uid的奇偶来进行
        List<String> result = new ArrayList<>();
        // 获取逻辑表名 sys_user   而我们的真实表为sys_user1  sys_user2
        String logicTableName = complexKeysShardingValue.getLogicTableName();
        for (Long uid : uidList) {
            String resultTableName = logicTableName + ((uid + 1) % 4 /2 + 1);
            if (!result.contains(resultTableName)) {
                result.add(resultTableName);
            }

        }
        return result;
    }

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

    @Override
    public void init(Properties properties) {
        this.props = props;
    }
}



在核心的dosharding方法当中,就可以按照我们之前的规则进行判断。不满足规则,直接抛出UnsupportedShardingOperationException异常,就可以组织ShardingSphere把SQL分配到真实数据库中执行。

接下来,还是需要增加策略配置,让course表按照这个规则进行分片。

# 使用CLASS_BASED分片算法- 不用配置SPI扩展文件
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.type=CLASS_BASED
# 指定策略 STANDARD|COMPLEX|HINT
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.props.strategy=COMPLEX
# 指定算法实现类。这个类必须是指定的策略对应的算法接口的实现类。 
# STANDARD-> StandardShardingAlgorithm;  COMPLEX->ComplexKeysShardingAlgorithm;  HINT -> HintShardingAlgorithm
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.props.algorithmClassName=com.roy.shardingDemo.algorithm.MyComplexAlgorithm




在这里插入图片描述

此时,新增或查询就都会经过上方我们自定义的MyComplexAlgorithm.doSharding()方法来选择具体的分表策略了



HINT_INLINE强制分片算法

使用常见,查询虚拟字段的情况,HINT强制路由可以用一种与SQL无关的方式进行分库分表。



就比如我仅仅查询uid是奇数的情况,如果是单机就可以直接在Mapper接口中添加下面的sql即可

@Select("select * from sys_user where MOD(uid,2)=1")

我们使用ShardingSphere当然也能查询,只不过又是全分片路由查询。因为此时我们是根据uid的奇偶来进行分片的,所以自然是希望只去查询某一个真实表,这种基于虚拟列的查询语句,ShardingSphere不知道该去进行解析,那么就只能是全路由查询了。

实际上ShardingSphere无法正常解析的语句还有很多。基本上用上分库分表后,你的应用就应该要和各种多表关联查询、多层嵌套子查询、distinct查询等各种复杂查询分手了。



这个uid的奇偶关系并不能通过SQL语句正常体现出来,这时,就需要用上ShardingSphere提供的另外一种分片算法HINT强制路由。HINT强制路由可以用一种与SQL无关的方式进行分库分表。



注释掉之前给course表分配的分表算法,重新分配一个HINT_INLINE类型的分表算法

#给sys_user表指定分表策略 sys_user为虚拟表 可以修改  hint-与SQL无关的方式进行分片
spring.shardingsphere.rules.sharding.tables.sys_user.table-strategy.hint.sharding-algorithm-name=sys_user_tbl_alg
# 分表策略-HINT:用于SQL无关的方式分表,使用value关键字。  sys_user_tbl_alg 为自定义string
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.type=HINT_INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.sys_user_tbl_alg.props.algorithm-expression=sys_user$->{value}

在这里插入图片描述



然后,在应用进行查询时,使用HintManager给HINT策略指定value的值。

@Test
public void queryCourseByHint(){
    HintManager hintManager = HintManager.getInstance();
    // 强制查sys_user1表
    hintManager.addTableShardingValue("sys_user","1");

    try {
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    } finally {

        //线程安全,所有用完要注意关闭。
        hintManager.close();
        //hintManager关闭的主要作用是清除ThreadLocal,释放内存。HintManager实现了AutoCloseable接口,
        // 所以建议使用try-resource的方式,用完自动关闭。
        //try(HintManager hintManager = HintManager.getInstance()){ xxxx }
    }


}



注意事项

  • 实体类分片键属性名不要使用id,mybatisplus会自动识别id,并为它使用雪花算法生成值。如果想使用ShardingSphere的雪花算法生成值就需要注意这个问题

  • ShardingSphere的雪花算法不是自增长的,对它进行取模分片时可能出现数据分片不均匀情况,可以自定义雪花算法

  • 不要使用批量插入

    insert into course values (1,'java',1001,1),(2,'java',1001,1);
    

    但是这样的SQL语句如果交给ShardingSphere去执行,那么这个语句就会造成困惑。到底应该往course_1还是course_2里插入?对于这种没有办法正确执行的SQL语句,ShardingSphere就会抛出异常。Insert statement does not support sharding table routing to multiple data nodes.

  • 简单inline分片算法,默认支持单分片键的 = in 精确查询,默认情况下不支持范围查询。需要开启一个配置

    allow-range-query-with-inline-sharding=true

  • standard标准分片算法,支持精确查询和范围查询

  • complex_inline 复杂分片算法,支持多分片键。但是建议分片键的类型是统一的

  • class_based自定义分片算法,比较灵活,可以通过java代码来指定使用的分片逻辑。可以通过实现相应的接口来实现该功能

  • hint_inline强制分片算法,直接在代码中强制指定查询的分片逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1975339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【传知代码】Flan-T5 使用指南(论文复现)

当今&#xff0c;自然语言处理技术正在以前所未有的速度和精度发展。在这个领域中&#xff0c;Flan-T5作为一种新兴的预训练语言模型&#xff0c;正吸引着广泛的关注和应用。Flan-T5不仅仅是一个强大的文本生成工具&#xff0c;它还能通过提供高效的语义理解和多任务学习能力&a…

【java计算机毕设】智慧校园管理系统MySQL springboot vue HTML maven寒暑假小组设计项目源码作业带文档安装环境

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】智慧校园管理系统MySQL springboot vue HTML maven寒暑假小组设计项目源码作业带文档安装环境 2项目介绍 系统功能&#xff1a; 智慧校园管理系统包括管理员、用户、老师三种角色。 管理员功能包括个人中心…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 团队关系判定(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题…

大语言模型学习笔记 LangChain简述

一、简述 LangChain是一个由大型语言模型 (LLM) 驱动的应用程序开发框架。LangChain 旨在为开发人员提供一系列功能&#xff0c;利用大型语言模型简化应用程序的创建和管理。 LangChain 可充当几乎所有 LLM 的通用接口&#xff0c;提供集中式开发环境来构建 LLM 应用程序并将其…

二十天刷leetcode【hot100】算法- day1[前端Typescript]

哈希表 1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。你…

适用于个人使用的十大数据恢复工具:综合指南

有许多数据恢复工具和软件可用于帮助恢复丢失或损坏的文件。通过了解您的需求并考虑这里探讨的工具&#xff0c;您将能够选择最佳的数据恢复软件&#xff0c;并希望找回您丢失的宝藏。在本综合指南中&#xff0c;我们将探索个人使用的十大数据恢复工具&#xff0c;重点介绍它们…

ESP32人脸识别开发- 基础介绍(一)

一、ESP32人脸识别的方案介绍 目前ESP32和ESP32S3都是支持的&#xff0c;官方推的开发板有两种&#xff0c;一种 ESP-EYE ,没有LCD 另一种是ESP32S3-EYE,有带LCD屏 二、ESP32人脸识别选用ESP32的优势 ESP32S3带AI 加速功能&#xff0c;在人脸识别的速度是比ESP32快了不少 | S…

鸿蒙 IM 即时通讯开发实践,融云 IM HarmonyOS NEXT 版

融云完成针对“纯血鸿蒙”操作系统的 SDK 研发&#xff0c;HarmonyOS NEXT 版融云 IM SDK 已上线&#xff0c;开发者可在“鸿蒙生态伙伴 SDK 市场”查询使用。 发挥 20 年通信行业技术积累和领创品牌效应&#xff0c;融云为社交、娱乐、游戏、电商、出行、医疗等各行业提供专业…

【深澜计费管理系统存在任意文件读取漏洞复现和检测脚本】

目录 一、漏洞介绍 二、poc利用 三、批量检测脚本 一、漏洞介绍 深澜计费管理系统存在任意文件读取漏洞。攻击者可以利用这个漏洞读取服务器上的任何文件,包括配置文件、源代码文件和敏感数据等。 二、poc利用 360 quake&#xff1a; favicon: "1fc27943c1f0d9b54cc…

modin,一个强大的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个强大的 Python 库 - modin。 Github地址&#xff1a;https://github.com/modin-project/modin 在数据科学和机器学习的领域中&#xff0c;Pandas 是一个非常流行的数据处理…

Excel第33享:借助易用宝将多个表格合并到一个表格

1、需求描述 现有3个销售数据的Excel表格&#xff0c;希望将其整合到一个表格里&#xff0c;如下图所示。 2、具体操作 &#xff08;1&#xff09;下载一个插件“易用宝”。 下载地址&#xff1a;最新版下载 | Excel易用宝 如果本地址失效&#xff0c;可以直接百度搜索“易…

2024年企业电脑加密系统排名|电脑加密系统TOP8

随着信息技术的发展&#xff0c;数据安全变得尤为重要。企业需要可靠的加密系统来保护敏感信息不受内部或外部威胁。以下是我为您推荐的8款企业电脑加密系统&#xff0c;都是知名的产品。 1.安企神系统 简介&#xff1a;安企神是一款专注于企业数据安全管理的综合性软件&#…

你的网站访客来自何方?GoAccess地理分析工具告诉你!

你的网站访客来自何方?GoAccess地理分析工具告诉你! 致读者: 点击上方 “雪之梦技术驿站” → 点击右上角“ … ”→ 点选“设为星标★ ” 加上星标,就不会找不到我啦!想象一下,你站在世界地图前,手中的放大镜缓缓移动,寻找着那些隐藏在数字海洋中的神秘岛屿——IP地址…

下一个十年能否赚到钱,就看你消化得了这篇文章不

所有的互联网创业者在2024年都会迎来前所未有的挑战&#xff0c;因为中国经济正在发生翻天覆地的变化&#xff0c;人口红利的消失&#xff0c;流量红利的消失&#xff0c;也就是说我们从一个增量时代到存量时代的过渡中&#xff0c;所以这两年我们可以清晰的看到很多传统老板正…

【传知代码】一款轻量级的隐私保护推荐系统(论文复现)

在当今信息爆炸的时代&#xff0c;个性化推荐系统在我们的日常生活中扮演着越来越重要的角色。然而&#xff0c;随着个人数据的广泛收集和利用&#xff0c;隐私保护成为了一个不可忽视的挑战。为了平衡推荐系统的效用与用户隐私的保护&#xff0c;一款轻量级的隐私保护推荐系统…

装win7出现0x0000007b蓝屏原因分析及解决方法

最近有网友问我装win7出现0x0000007b蓝屏怎么办&#xff1f;0x0000007b电脑蓝屏通常情况是硬盘的存储控制器驱动加载错误导致故障。出现0x0000007b蓝屏代码的原因有很多比如硬盘模式、安装的系统没有集成相关的磁盘控制器驱动等&#xff0c;下面小编就教大家装win7出现0x000000…

VFS(虚拟文件系统)是什么,有真实的文件系统吗?

文章目录 1 VFS&#xff08;虚拟文件系统&#xff09;是什么1.1 VFS 的设计目的 2 VFS 的工作原理2.1 VFS 的文件操作流程 3 真实的文件系统3.1 常见的真实文件系统3.2 真实文件系统与 VFS 的关系 4 总结封面 1 VFS&#xff08;虚拟文件系统&#xff09;是什么 虚拟文件系统&a…

水论文如何找创新,优秀者模仿,末尾有例子示范_来自B站水论文的程序猿

系列文章目录 文章目录 系列文章目录一、水论文如何找创新关键在“找”1、相似领域找2、找到后3、如何稍微改动4、 自己领域找 二、示例 一、水论文如何找创新关键在“找” 1、相似领域找 比如某人研究视频描述中的单句视频描述&#xff0c;他可以去密集视频描述领域找相关论…

springMVC中从Excel文件中导入导出数据

目录 1. 数据库展示2. 导入依赖3. 写方法3.1 导入数据3.2 导出数据 4. 效果5. 不足6. 参考链接 1. 数据库展示 2. 导入依赖 pom.xml <!--文件上传处理--><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId>&…

邻接表(链式存储)实现图的存储

目录 一. 前言 二. 用邻接表创建无向网的完整源代码 一. 前言 无向图邻接表的形式如下所示&#xff1a; 其中类如v1的结点为头结点&#xff0c;头结点后面的结点为边结点&#xff0c;表示与头结点中顶点相连的边的信息。 采用邻接表创建无向网的算法思路&#xff1a; 1&#…