shardingsphere第二课-shardingsphere-jdbc的基本使用及各种分片策略

news2024/11/15 19:28:58

第一章介绍

一、ShardingJDBC客户端分库分表

ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template
    或直接使用JDBC;
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
  • 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用JDBC
    访问的数据库。

在这里插入图片描述

二、ShardingProxy服务端分库分表

ShardingSphere-Proxy 定位为透明化的数据库代理端,通过实现数据库二进制协议,对异构语言提供支持。 目前提供 MySQL 和 PostgreSQL 协议,透明化数据库操作,对 DBA 更加友好。

  • 向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用;
  • 兼容 MariaDB 等基于 MySQL 协议的数据库,以及 openGauss 等基于 PostgreSQL 协议的数据库;
  • 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端,如:MySQL Command Client, MySQL Workbench,Navicat 等。
    在这里插入图片描述

三、ShardingSphere混合部署架构
这两个产品都各有优势。ShardingJDBC跟客户端在一起,使用更加灵活。而ShardingProxy是一个独立部署的服务,所以他的功能相对就比较固定。他们的整体区别如下:
在这里插入图片描述
另外,在产品图中,Governance Center也是其中重要的部分。他的作用有点类似于微服务架构中的配置中心,可以使用第三方服务统一管理分库分表的配置信息,当前建议使用的第三方服务是Zookeeper,同时也支持Nacos,Etcd等其他第三方产品。

由于ShardingJDBC和ShardingProxy都支持通过Governance Center,将配置信息交个第三方服务管理,因此,也就自然支持了通过Governance Center进行整合的混合部署架构。

在这里插入图片描述

第二章搭建

用最常用的SpringBoot+MyBatis+MyBatis-plus快速搭建一个可以访问数据的简单应用,以这个应用作为我们分库分表的基础。

  1. step1: 在数据库中创建course表,建表语句如下:
CREATE TABLE course (
`cid` bigint(0) NOT NULL AUTO_INCREMENT,
`cname` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_id` bigint(0) NOT NULL,
`cstatus` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`cid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  1. step2: 搭建一个Maven项目,在pom.xml中加入依赖,其中就包含访问数据库最为简单的几个组件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ShardingDemoV5</artifactId>
        <groupId>com.roy</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Sharding</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- 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>
        <!-- 与COSID集成 -->
<!--        <dependency>-->
<!--            <groupId>me.ahoo.cosid</groupId>-->
<!--            <artifactId>cosid-spring-boot-starter</artifactId>-->
<!--            <version>1.18.6</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>me.ahoo.cosid</groupId>-->
<!--            <artifactId>cosid-zookeeper</artifactId>-->
<!--            <version>1.18.6</version>-->
<!--        </dependency>-->
        <!--XA 分布式事务 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-xa-core</artifactId>
            <version>5.2.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>transactions-jdbc</artifactId>
                    <groupId>com.atomikos</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>transactions-jta</artifactId>
                    <groupId>com.atomikos</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 版本滞后了 -->
        <dependency>
            <artifactId>transactions-jdbc</artifactId>
            <groupId>com.atomikos</groupId>
            <version>5.0.8</version>
        </dependency>
        <dependency>
            <artifactId>transactions-jta</artifactId>
            <groupId>com.atomikos</groupId>
            <version>5.0.8</version>
        </dependency>

        <!-- 使用XA事务时,可以引入其他几种事务管理器 -->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-xa-bitronix</artifactId>-->
<!--            <version>5.2.1</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-xa-narayana</artifactId>-->
<!--            <version>5.2.1</version>-->
<!--        </dependency>-->
        <!--         Base 柔性事务-->
        <!--        <dependency>-->
        <!--            <groupId>org.apache.shardingsphere</groupId>-->
        <!--            <artifactId>sharding-transaction-base-seata-at</artifactId>-->
        <!--            <version>4.1.1</version>-->
        <!--        </dependency>-->
        <!--        <dependency>-->
        <!--            <groupId>io.seata</groupId>-->
        <!--            <artifactId>seata-all</artifactId>-->
        <!--            <version>1.4.0</version>-->
        <!--        </dependency>-->
        <!--        <dependency>-->
        <!--            <groupId>com.alibaba.nacos</groupId>-->
        <!--            <artifactId>nacos-client</artifactId>-->
        <!--            <version>1.4.1</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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </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>
        <!-- mysql连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- mybatisplus依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 将SPI扩展功能单独打成jar包 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <id>ShardingSPIDemo</id>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                        <configuration>
                            <classifier>spiextention</classifier>
                            <includes>
                                <include>com/roy/shardingDemo/algorithm/*</include>
                                <include>META-INF/services/*</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.1.RELEASE</version>
                <configuration>
                    <mainClass>com.roy.shardingDemo.ShardingJDBCApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
  1. step3: 使用MyBatis-plus的方式,直接声明Entity和Mapper,映射数据库中的course表。
public class Course {
private Long cid;
private String cname;
private Long userId;
private String cstatus;
//省略。getter ... setter ....
}

public interface CourseMapper extends BaseMapper<Course> {
}
  1. step4: 增加SpringBoot启动类,扫描mapper接口
@SpringBootApplication
@MapperScan("com.roy.jdbcdemo.mapper")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
  1. step5: 在springboot的配置文件application.properties中增加数据库配置
@SpringBootTest
@RunWith(SpringRunner.class)
public class JDBCTest {
@Resource
private CourseMapper courseMapper;
@Test
public void addcourse() {
for (int i = 0; i < 10; i++) {
Course c = new Course();
c.setCname("java");
c.setUserId(1001L);
c.setCstatus("1");
courseMapper.insert(c);
//insert into course values ....
System.out.println(c);
}
}

@Test
public void queryCourse() {
QueryWrapper<Course> wrapper = new QueryWrapper<Course>();
wrapper.eq("cid",1L);
List<Course> courses = courseMapper.selectList(wrapper);
courses.forEach(course ­> System.out.println(course));
}
}

自此基本的springboot项目搭建完成

二、开始引入ShardingSphere分库分表
1、先引入ShardingSphere依赖,上面pom已有
2、在对应数据库里创建分片表,按照上面的sql,但是去掉auto自增
在这里插入图片描述

3、增加ShardingJDBC的分库分表配置

# 打印SQL
spring.shardingsphere.props.sql­show = true
spring.main.allow­bean­definition­overriding = true
# ­­­­­­­­­­­­­­­­数据源配置
# 指定对应的真实库
spring.shardingsphere.datasource.names=m0,m1
# 配置真实库
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver­class­name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/coursedb?serverTimezone=UTC
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=root
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver­class­name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/coursedb2?serverTimezone=UTC
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
#­­­­­­­­­­­­­­­­­­­­­­­­分布式序列算法配置
# 雪花算法,生成Long类型主键。
spring.shardingsphere.rules.sharding.key­generators.alg_snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key­generators.alg_snowflake.props.worker.id=1

# 指定分布式主键生成策略
spring.shardingsphere.rules.sharding.tables.course.key­generate­strategy.column=cid
spring.shardingsphere.rules.sharding.tables.course.key­generate­strategy.key­generator-name=alg_snowflake
#­­­­­­­­­­­­­­­­­­­­­­­配置实际分片节点
spring.shardingsphere.rules.sharding.tables.course.actual­data­nodes=m$­>{0..1}.course_$­>{1..2}

#­­­­­­­­­­­­­­­­­­­­­­­配置分库策略,按cid取模
spring.shardingsphere.rules.sharding.tables.course.database­strategy.standard.sharding-column=cid
spring.shardingsphere.rules.sharding.tables.course.database­strategy.standard.sharding-algorithm­name=course_db_alg
spring.shardingsphere.rules.sharding.sharding­algorithms.course_db_alg.type=MOD
spring.shardingsphere.rules.sharding.sharding­algorithms.course_db_alg.props.sharding-count=2

#给course表指定分表策略 standard­按单一分片键进行精确或范围分片

spring.shardingsphere.rules.sharding.tables.course.table­strategy.standard.shardingcolumn=cid
spring.shardingsphere.rules.sharding.tables.course.table­strategy.standard.shardingalgorithm­name=course_tbl_alg
# 分表策略­INLINE:按单一分片键分表
spring.shardingsphere.rules.sharding.sharding­algorithms.course_tbl_alg.type=INLINE
spring.shardingsphere.rules.sharding.sharding­algorithms.course_tbl_alg.props.algorithmexpression=course_$­>{cid%2+1}

接下来再次执行addcourse的单元测试,就能看到,十条课程信息被按照cid的奇偶,拆分到了m0.course_1和m1.course_2两张表中。
在这里插入图片描述

  • 这个示例中,course信息只能平均分到两个表中,而无法均匀分到四个表中。这其实是根据cid进行计算的结果。而将course_tbl_alg的计算表达式改成 course_$->{((cid+1)%4).intdiv(2)+1} 后,理论上,如果cid是连续 递增的,就可以将数据均匀分到四个表里。但是snowsake雪花算法生成的ID并不是连续的,所以有时候还 是无法分到四个表。
#spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.algorithm-expression=course_$->{((cid+1)%4).intdiv(2)+1}

补充:尽量不要使用批量插入。
在这里插入图片描述

三、理解ShardingSphere的核心概念

  1. 虚拟库: ShardingSphere的核心就是提供一个具备分库分表功能的虚拟库,他是一个ShardingSphereDatasource实例。应用程序只需要像操作单数据源一样访问这个ShardingSphereDatasource即可。
  2. 真实库: 实际保存数据的数据库。这些数据库都被包含在ShardingSphereDatasource实例当中,由ShardingSphere决定未来需要使用哪个真实库。
  3. 逻辑表: 应用程序直接操作的逻辑表。
  4. 真实表: 实际保存数据的表。这些真实表与逻辑表表名不需要一致,但是需要有相同的表结构,可以分布在不同的真实库中。应用可以维护一个逻辑表与真实表的对应关系,所有的真实表默认也会映射成为ShardingSphere的虚拟表。
  5. 分布式主键生成算法: 给逻辑表生成唯一主键。由于逻辑表的数据是分布在多个真实表当中的,所有,单表的索引就无法保证逻辑表的ID唯一性。ShardingSphere集成了几种常见的基于单机生成的分布式主键生成器。比如SNOWFLAKE,COSID_SNOWFLAKE雪花算法可以生成单调递增的long类型的数字主键,还有UUID,NANOID可以生成字符串类型的主键。当然,ShardingSphere也支持应用自行扩展主键生成算法。比如基于Redis,Zookeeper等第三方服务,自行生成主键。
  6. 分片策略: 表示逻辑表要如何分配到真实库和真实表当中,分为分库策略和分表策略两个部分。分片策略由分片键和分片算法组成。分片键是进行数据水平拆分的关键字段。如果没有分片键,ShardingSphere将只能进行全路由,SQL执行的性能会非常差。分片算法则表示根据分片键如何寻找对应的真实库和真实表。简单的分片策略可以使用Groovy表达式直接配置,当然,ShardingSphere也支持自行扩展更为复杂的分片算法。

四、ShardingJDBC深入实战

1、简单INLINE分片算法
我们之前配置的简单分库分表策略已经可以根据自动生成的cid,将数据插入到不同的真实库当中。那么当然也支持按照cid进行数据查询。

 /**
     * 针对分片键进行精确查询,都可以使用表达式控制
     * 对于in查询,4.x遇到混合分片结果,会直接走全分片路由。 新版本会进行精确分片
     * select * from course where cid=xxx
     */
    @Test
    public void queryCourse() {
        QueryWrapper<Course> wrapper = new QueryWrapper<Course>();
        wrapper.eq("cid",3L);
//        wrapper.in("cid",851198095084486657L,851198095139012609L,851198095180955649L,4L);
        //带上排序条件不影响分片逻辑
        wrapper.orderByDesc("user_id");
        List<Course> courses = courseMapper.selectList(wrapper);
        courses.forEach(course -> System.out.println(course));
    }

像= 和 in 这样的操作,可以拿到cid的精确值,所以都可以直接通过表达式计算出可能的真实库以及真实表,ShardingSphere就会将逻辑SQL转去查询对应的真实库和真实表。这些查询的策略,只要配置了sql-show参数,都会打印在日志当中。

2、STANDARD标准分片算法

应用当中我们可能对于主键信息不只是进行精确查询,还需要进行范围查询。例如:

   @Test
    public void queryCourseRange(){
        //select * from course where cid between xxx and xxx
        QueryWrapper<Course> wrapper = new QueryWrapper<>();
        wrapper.between("cid",799020475735871489L,799020475802980353L);
        List<Course> courses = courseMapper.selectList(wrapper);
        courses.forEach(course -> System.out.println(course));
    }

这时,如果直接执行,那么由于ShardingSphere无法根据配置的表达式计算出可能的分片值,所以执行时他就会抛出一个异常。
在这里插入图片描述
需要配置强行开始

spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.allow-range-query-with-inline-sharding=true

加上这个参数后,就可以进行查询了。但是这样就可以了吗?观察一下Actual SQL的执行方式,你会发现这时SQL还是按照全路由的方式执行的。之前一直强调过,这是效率最低的一种执行方式。那么有没有办法通过查询时的范围下限和范围上限自己计算出一个目标真实库和真实表呢?当然是支持的。记住这个问题。

在这里插入图片描述
3、COMPLEX_INLINE复杂分片算法

    /**
     * 使用COMPLEX_INLINE策略,使用多个分片键进行组合路由
     * cid和user_id进行组合分片
     */
    @Test
    public void queryCourseComplexSimple(){
        QueryWrapper<Course> wrapper = new QueryWrapper<Course>();
//        wrapper.orderByDesc("user_id");
        wrapper.in("cid",851198095084486657L,851198095139012609L);
//        wrapper.between("cid",799020475735871489L,799020475802980353L);
        wrapper.eq("user_id",1001L);
        List<Course> course = courseMapper.selectList(wrapper);
        //select * fro couse where cid in (xxx) and user_id between(8,3)
        System.out.println(course);
    }

执行一下,这当然是可以的。但是有一个小问题,user_id查询条件只能参与数据查询,但是并不能参与到分片算
法当中。例如在我们的示例当中,所有的user_id都是1001L,这其实是数据一个非常明显的分片规律。如果user_id的查询条件不是1001L,那这时其实不需要到数据库中去查,我们也能知道是不会有结果的。有没有办法让user_id也参与到分片算法当中呢?
当然是可以的, 不过STANDARD策略就不够用了。这时候就需要引入COMPLEX_INLINE策略。注释掉之前给course表配置的分表策略,重新分配一个新的分表策略:
在这里插入图片描述


#给course表指定分表策略  complex-按多个分片键进行组合分片
spring.shardingsphere.rules.sharding.tables.course.table-strategy.complex.sharding-columns=cid,user_id
spring.shardingsphere.rules.sharding.tables.course.table-strategy.complex.sharding-algorithm-name=course_tbl_alg
# 分表策略-COMPLEX:按多个分片键组合分表
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.type=COMPLEX_INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.algorithm-expression=course_$->{(cid+user_id)%2+1}

4、CLASS_BASED自定义分片算法
我们测试数据中的user_id都是固定的1001L,那么接下来我就可以希望在对user_id进行范围查询时,能够提前判断一些不合理的查询条件。而具体的判断规则,比如在对user_id进行between范围查询时,要求查询的下限不能超过查询上限,并且查询的范围必须包括1001L这个值。如果不满足这样的规则,那么就希望这个SQL语句就不要去数据库执行了。因为明显是不可能有数据的,还非要去数据库查一次,明显是浪费性能。
虽然对于COMPLEX_INLINE策略,也支持添加allow-range-query-with-inline-sharding参数让他能够支持分片键的范围查询,但是这时这种复杂的分片策略就明显不能再用一个简单的表达式来忽悠了。

package com.roy.shardingDemo.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中再做类型转换。这样是为了防止分片键类型与算法声明的类型不符合。
 * @auth roykingw
 */
public class MyComplexAlgorithm implements ComplexKeysShardingAlgorithm<Long> {

    private static final String SHARING_COLUMNS_KEY = "sharding-columns";

    private Properties props;
    //保留配置的分片键。在当前算法中其实是没有用的。
    private Collection<String> shardingColumns;

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

    /**
     * 实现自定义分片算法
     * @param availableTargetNames 在actual-nodes中配置了的所有数据分片
     * @param shardingValue 组合分片键
     * @return 目标分片
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Long> shardingValue) {
        //select * from cid where cid in (xxx,xxx,xxx) and user_id between {lowerEndpoint} and {upperEndpoint};
        Collection<Long> cidCol = shardingValue.getColumnNameAndShardingValuesMap().get("cid");
        Range<Long> userIdRange = shardingValue.getColumnNameAndRangeValuesMap().get("user_id");
        //拿到user_id的查询范围
        Long lowerEndpoint = userIdRange.lowerEndpoint();
        Long upperEndpoint = userIdRange.upperEndpoint();
        //如果下限 》= 上限
        if(lowerEndpoint >= upperEndpoint){
            //抛出异常,终止去数据库查询的操作
            throw new UnsupportedShardingOperationException("empty record query","course");
            //如果查询范围明显不包含1001
        }else if(upperEndpoint<1001L || lowerEndpoint>1001L){
            //抛出异常,终止去数据库查询的操作
            throw new UnsupportedShardingOperationException("error range query param","course");
//            return result;
        }else{
            List<String> result = new ArrayList<>();
            //user_id范围包含了1001后,就按照cid的奇偶分片
            String logicTableName = shardingValue.getLogicTableName();//操作的逻辑表 course
            for (Long cidVal : cidCol) {
                String targetTable = logicTableName+"_"+(cidVal%2+1);
                if(availableTargetNames.contains(targetTable)){
                    result.add(targetTable);
                }
            }
            return result;
        }
    }

    private Collection<String> getShardingColumns(final Properties props) {
        String shardingColumns = props.getProperty(SHARING_COLUMNS_KEY, "");
        return shardingColumns.isEmpty() ? Collections.emptyList() : Arrays.asList(shardingColumns.split(","));
    }

    public void setProps(Properties props) {
        this.props = props;
    }
    @Override
    public Properties getProps() {
        return this.props;
    }
    @Override
    public String getType(){
        return "MYCOMPLEX";
    }
}

在这里插入图片描述

#给course表指定分表策略  complex-按多个分片键进行组合分片
spring.shardingsphere.rules.sharding.tables.course.table-strategy.complex.sharding-columns=cid,user_id
spring.shardingsphere.rules.sharding.tables.course.table-strategy.complex.sharding-algorithm-name=course_tbl_alg
# 分表策略-COMPLEX:按多个分片键组合分表
#spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.type=COMPLEX_INLINE
#spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.algorithm-expression=course_$->{(cid+user_id)%2+1}
# 扩展自定义的复杂分片算法- 模仿COMPLEX_INLINE的实现方式。
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.type=MYCOMPLEX
# 使用CLASS_BASED分片算法- 不用配置SPI扩展文件
#spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.type=CLASS_BASED
# 指定策略 STANDARD|COMPLEX|HINT
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.strategy=COMPLEX
# 指定算法实现类。这个类必须是指定的策略对应的算法接口的实现类。 STANDARD-> StandardShardingAlgorithm;COMPLEX->ComplexKeysShardingAlgorithm;HINT -> HintShardingAlgorithm
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.algorithmClassName=com.roy.shardingDemo.algorithm.MyComplexAlgorithm

5、HINT_INLINE强制分片算法
接下来我们把查询场景再进一步,需要查询所有cid为奇数的课程信息。这要怎么查呢?

public interface CourseMapper extends BaseMapper<Course> {
@Select("select * from course where MOD(cid,2)=1")
List<Long> unsupportSql();
}
@Test
public void unsupportTest(){
//select * from course where mod(cid,2)=1
List<Long> res = courseMapper.unsupportSql();
res.forEach(System.out::println);
}

执行结果当然是没有问题。但是你会发现,分片的问题又出来了。在我们当前的这个场景下,course的信息就是按照cid的奇偶分片的,所以自然是希望只去查某一个真实表就可以了。这种基于虚拟列的查询语句,对于ShardingSphere来说实际上是一块难啃的骨头。因为他很难解析出你是按照cid分片键进行查询的,并且不知道怎么组织对应的策略去进行分库分表。所以他的做法只能又是性能最低的全路由查询。

HINT强制路由可以用一种与SQL无关的方式进行分库分表。
注释掉之前给course表分配的分表算法,重新分配一个HINT_INLINE类型的分表算法

 /**
     * 使用HINT强制路由策略。
     * 脱离SQL自己指定分片策略。
     * 强制查询course_1表
     */
    @Test
    public void queryCourseByHint(){
        //强制只查course_1表
        HintManager hintManager = HintManager.getInstance();
        //注意这两个属性,dataSourceBaseShardingValue用于强制分库
        // 强制查course_1表
//        hintManager.setDatabaseShardingValue(1L);
        hintManager.addTableShardingValue("course","1");
        //select * from course;
        List<Course> courses = courseMapper.selectList(null);
        courses.forEach(course -> System.out.println(course));
        //线程安全,所有用完要注意关闭。
        hintManager.close();
        //hintManager关闭的主要作用是清除ThreadLocal,释放内存。HintManager实现了AutoCloseable接口,所以建议使用try-resource的方式,用完自动关闭。
        //try(HintManager hintManager = HintManager.getInstance()){ xxxx }
    }

在这里插入图片描述

#给course表指定分表策略  hint-与SQL无关的方式进行分片
spring.shardingsphere.rules.sharding.tables.course.table-strategy.hint.sharding-algorithm-name=course_tbl_alg
# 分表策略-HINT:用于SQL无关的方式分表,使用value关键字。
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.type=HINT_INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.algorithm-expression=course_$->{value}

五、总结

在之前的示例中就介绍了ShardingSphere提供的MOD、HASH-MOD这样的简单内置分片策略,standard、complex、hint三种典型的分片策略以及CLASS_BASED这种扩展分片策略的方法。为什么要有这么多的分片策略,其实就是以为分库分表面临的业务场景其实是很复杂的。即便是ShardingSphere,也无法真的像MySQL、Oracle这样的数据库产品一样,完美的兼容所有的SQL语句。因此,一旦开始决定用分库分表,那么后续业务中的每一个SQL语句就都需要结合分片策略进行思考,不能像操作真正数据库那样随心所欲了。

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

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

相关文章

chatgpt赋能python:Python中如何输入一个列表

Python中如何输入一个列表 输入一个列表是Python编程的基本任务之一。列表可以看做是一种序列&#xff0c;其中包含多个元素&#xff0c;用逗号隔开&#xff0c;并用方括号括起来。在Python中&#xff0c;列表是一种非常常见的数据类型&#xff0c;常用于存储和处理一系列相关…

断言操作符介绍

目录 1.延时操作符&#xff08;##&#xff09; 1.1 ##m 1.2 ##[m:n] 2.蕴含操作符&#xff08;|>,|->&#xff09; 2.1 |>操作符 2.2 |->操作符 3 重复操作符 ([*m][->m][m]) 3.1 连续重复操作符&#xff08;[*m][*m:n]&#xff09; 3.2 跟随重复操作…

Java选择题刷题记录5

Java堆栈 图片来自https://www.cnblogs.com/cici-new/p/14963762.html 数组、String都在堆里 枚举类 1.枚举类可以实现一个或多个接口&#xff0c;使用enum定义的枚举类默认继承java.lang.Enum类&#xff0c;而不是默认继承Object类&#xff0c;其中 java.lang.Enum类实现了…

机器视觉硬件的选择-标定板

康耐视智能相机Insight-缺陷检测 一>棋盘格的作用 a>畸变校正 径向畸变,径向畸变就是沿着透镜半径方向分布的畸变,产生原因是光线在原理透镜中心的地方比靠近中心的地方更加弯曲,这种畸变在短焦镜头中表现更加明显,径 向畸变主要包括桶形畸变和枕形畸变两种。以下分别…

chatgpt赋能python:Python输入π的方法及其应用

Python输入π的方法及其应用 Python是一门强大的编程语言&#xff0c;其支持的数学函数功能能够帮助用户完成各种复杂的计算操作。当我们需要在Python代码中使用π值时&#xff0c;不同的场景需要不同的处理方法。本文将详细介绍如何在Python中输入π值&#xff0c;并且探讨它…

Ansible 的脚本 --- playbook 剧本

playbook的相关知识 playbook 的简介 playbook是 一个不同于使用Ansible命令行执行方式的模式&#xff0c;其功能更强大灵活。 简单来说&#xff0c;playbook是一个非常简单的配置管理和多主机部署系统&#xff0c; 不同于任何已经存在的模式&#xff0c;可作为一个适合部署…

【C++】STL之string类(2)

个人主页&#xff1a;平行线也会相交&#x1f4aa; 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】&#x1f48c; 本专栏旨在记录C的学习路线&#xff0c;望对大家有所帮助&#x1f647;‍ 希望我们一起努力、成长&…

Vue中如何进行分布式任务调度与任务监控

Vue中如何进行分布式任务调度与任务监控 在复杂的系统中&#xff0c;如何有效地进行任务调度和监控是一个非常重要的问题。分布式系统中&#xff0c;任务调度和监控则更加复杂。Vue是一款流行的前端框架&#xff0c;本文将介绍如何在Vue中进行分布式任务调度和监控。 什么是分…

QML基础

从 Qt 4.7 开始&#xff0c;Qt 引入了一种声明式脚本语言&#xff0c;称为 QML&#xff08;Qt Meta Language 或者 Qt Modeling Language&#xff09;&#xff0c;作为 C 语言的一种替代。而 Qt Quick 就是使用 QML 构建的一套类库。 QML 是一种基于 JavaScript 的声明式语言。…

Qt下存储读写应用程序设置的三种方法

一、简介 List item 用户对应用程序经常有这样的要求&#xff1a;要求它能记住它的settings&#xff0c;比如窗口大小、位置和密码等等。有三种方法可以实现&#xff1a; 使用注册表&#xff1b;使用配置文件(.ini)&#xff1b;使用自定义文件(例如.txt)。 二、使用注册表 …

C盘空间不足清理方法 之 Google Chrome 浏览器用户数据迁移和Windows10 默认浏览器路径失效修复

原理分析 将原来C盘的目录拷贝到其他盘&#xff0c;然后用mklink建立一个联接&#xff0c;这里贴下ChatGPT对于三种链接的解释 # 在Windows 10中&#xff0c;mklink是一个命令行工具&#xff0c;用于创建符号链接&#xff08;symbolic link&#xff09;或者硬链接&#xff08…

chatgpt赋能python:Python异常过滤教程:如何正确处理和过滤Python中的异常

Python异常过滤教程&#xff1a;如何正确处理和过滤Python中的异常 介绍 Python是一种非常流行的编程语言&#xff0c;广泛应用于各种应用程序和领域中。在我们编写Python程序的过程中&#xff0c;通常要处理各种异常情况&#xff0c;比如用户输入错误&#xff0c;文件读取错…

CSS查缺补漏之《Web字体、2D/3D变换》

文章略长&#xff0c;慢慢享用&#xff5e; Web字体 css3新增了字体&#xff0c;使得用户不必局限在本计算机中安装的字体&#xff0c;可以使用多种字体&#xff1b; 需要在style中定义font-face规则&#xff1b; font-face { font-family: xxx名字; /* 必选项&#xff0c;自…

ROS2 入门应用 创建启动文件(Python)

ROS2 入门应用 创建启动文件&#xff08;Python&#xff09; 1. 创建功能包2. 添加依赖关系3. 添加启动文件4. 创建启动文件4.1. Python4.2. XML4.3. YAML 5. 编译和运行 1. 创建功能包 用Python、XML或YAML编写的启动文件可以启动和停止不同的节点&#xff0c;以及触发和处理…

设计模式(二十三):行为型之解释器模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

chapter8:SpringBoot启动配置原理

尚硅谷SpringBoot顶尖教程 1. 启动流程简介 SpringBoot应用从主启动类启动后的运行流程主要包含下面几个要点&#xff1a; &#xff08;1&#xff09;准备环境 执行ApplicationContextInitializer#initialize()方法&#xff1b;监听器SpringApplicationRunListener回调cont…

ADC 读取电位器旋钮,用回差消除临界值档位跳动

就是比如&#xff0c;用电位器当旋钮做风扇调速&#xff0c;划分出10 个速度档位&#xff0c;对应10 个ADC 转换结果的阈值。如果直接比较阈值&#xff0c;当旋钮拧到临近阈值的地方时&#xff0c;ADC 结果的微小跳动会导致风扇档位在两个级别之间不停左右横跳&#xff0c;因此…

Linux MySQL 索引 事务 存储引擎 死锁

索引&#xff08;面试问得多&#xff09; 索引是一个排序的列表&#xff0c;包含索引字段的值和其相对应的行数据所在的物理地址 作用 加快表的查询速度&#xff0c;还可以对字段排序 如何实现的搜索加速&#xff1f; 没有索引的情况下&#xff0c;要查询某行数据&#xff0c;需…

代码审计——命令执行详解

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 漏洞描述02 审计要点03 漏洞特征04 漏洞案例05 修复方案 01 漏洞描述 命令注入是指因为系统使用了可以执行命令的危险函数&#xff0c;但是调用这些函数的参数可控&#xff0c;并没有做过滤或过滤不…

Flutter集成Umeng步骤及若干问题总结

由于Flutter项目中用到umeng统计及手机号一键登录功能&#xff0c;但实际集成使用中遇到各种坑&#xff0c;文档及demo却都没有提及&#xff0c;因此写下这篇文章&#xff0c;有遇到同样问题的同学可以参考下。 集成之前&#xff0c;最好先查看一下文档&#xff1a;https://de…