Sharding-Sphere学习专题(二)案例和分片策略

发布于:2025-07-16 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

一、第一个分库分表的案例

0、准备数据库

 1、快速搭建基础JDBC应用

2、引入ShardingJDBC快速实现分库分表

二、理解分库分表的核心概念

1、ShardingSphere分库分表的核心概念

 2、垂直分片和水平分片

三、ShardingJDBC常见数据分片策略实战 

1、INLINE简单分片

2、STANDARD标准分片

3、COMPLEX_INLINE复杂分片

4、CLASS_BASED自定义分片

5、HINT_INLINE强制分片

6、常用分片算法总结


一、第一个分库分表的案例

0、准备数据库

开发之前,需要提前准备一个MySQL数据库,并在其中创建Course表。Course表的建表语句如下:

CREATE TABLE course  (
  `cid` bigint(0) NOT NULL,
  `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、快速搭建基础JDBC应用

(1)搭建一个Maven项目,在pom.xml中加入依赖,其中就包含访问数据库最为简单的几个组件。

  <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
​
            <!-- mybatisplus依赖 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.0.5</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.20</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- 数据源连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</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>

(2) 使用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> {
}

 (3)增加SpringBoot启动类,扫描mapper接口。

@SpringBootApplication
@MapperScan("com.roy.jdbcdemo.mapper")
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

(4)在springboot的配置文件application.properties中增加数据库配置。

spring.datasource.druid.db-type=mysql
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://192.168.65.212:3306/test?serverTimezone=UTC
spring.datasource.druid.username=root
spring.datasource.druid.password=root

(5) 做一个单元测试,简单的把course课程信息插入到数据库,以及从数据库中进行查询。

@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));
    }
}

2、引入ShardingJDBC快速实现分库分表

接下来,在之前的简单案例基础上,快速使用ShardingSphere实现Course表的分库分表功能。体验一下ShardingSphere是如何让分库分表这个事情变简单的。

(1)在pom.xml中引入ShardingSphere

<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>
        <!-- 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>
        <!-- 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>
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

(2)在对应数据库里创建分片表

按照我们之前的设计,去对应的数据库中自行创建course_1和course_2表。表结构与course表是一致的。

(3)增加ShardingJDBC的分库分表配置

然后,好玩的事情来了。应用层代码不需要做任何的修改,直接修改SpringBoot的配置文件application.properties,在里面添加以下配置信息。再次执行addCourse方法,添加课程信息,数据就会自动完成分库分表。

# 打印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://192.168.65.212:3306/shardingdb1?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://192.168.65.212:3306/shardingdb2?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 //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}
#MOD分库策略
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.sharding-column=cid
spring.shardingsphere.rules.sharding.tables.course.table-strategy.standard.sharding-algorithm-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.algorithm-expression=course_$->{cid%2+1}
#这种算法如果cid是严格递增的,就可以将数据均匀分到四个片。但是雪花算法并不是严格递增的。
#如果需要做到均匀分片,修改算法同时,还要修改雪花算法。把SNOWFLAKE换成MYSNOWFLAKE
#spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.algorithm-expression=course_$->{((cid+1)%4).intdiv(2)+1}

接下来主要观察addcourse方法的执行结果。可以看到,十条课程信息,cid字段自动生成了一些ID,并且数据按照cid的奇偶,拆分到了m0.course_1和m1.course_2 两张表中。

并且在日志里可以看到实际的执行情况::

image.png

在这个示例中,十条course信息只能平均分配到两个表中,而无法均匀分到四张表中。如果希望改变这个结果,让course信息分到四个表中,需要从数据和算法两个方面思考。

如果cid是连续增长的,那么,将分片算法course_db_alg的计算表达式修改为 course_$->{((cid+1)%4).intdiv(2)+1}理论上就可以了。

但是,事实上,snowflake雪花算法生成的ID并不是连续的,所以在这个示例中,依然是无法将数据分到四张表的。具体原因,会在后续分析分布式ID

二、理解分库分表的核心概念

1、ShardingSphere分库分表的核心概念

虚拟库

ShardingSphere的核心就是提供一个具备分库分表功能的虚拟库,他是一个ShardingSphereDatasource实例。应用程序只需要像操作单数据源一样访问这个ShardingSphereDatasource即可。示例中,MyBatis框架并没有特殊指定DataSource,就是使用的ShardingSphere的DataSource数据源。

真实库

实际保存数据的数据库。这些数据库都被包含在ShardingSphereDatasource实例当中,由ShardingSphere决定未来需要使用哪个真实库。示例中,m0和m1就是两个真实库。

逻辑表

应用程序直接操作的逻辑表。 示例中操作的course表就是一个逻辑表,并不需要在数据库中真实存在。

真实表

实际保存数据的表。这些真实表与逻辑表表名不需要一致,但是需要有相同的表结构,可以分布在不同的真实库中。应用可以维护一个逻辑表与真实表的对应关系,所有的真实表默认也会映射成为ShardingSphere的虚拟表。示例中course_1和course_2就是真实表。

分布式主键生成算法

给逻辑表生成唯一主键。由于逻辑表的数据是分布在多个真实表当中的,所有,单表的索引就无法保证逻辑表的ID唯一性。因此,在做分库分表时,通常都会独立出一个生成分布式ID的主键生成算法。示例中使用的SNOWFLAKE雪花算法就是一种很常见的主键生成算法。详情:雪花算法Snowflake-CSDN博客文章浏览阅读861次,点赞19次,收藏11次。用一张单独的表,每一行数据维护一个业务表的主键id,每次请求取一个step步长的id(图中1000),再去内存+1,max_id是上一次取出的最后一行的id数,leaf无状态的支持线性扩容。由于强依赖时钟,对时间的要求比较敏感,在机器工作时NTP同步也会造成秒级别的回退,建议可以直接关闭NTP同步。因为雪花算法根据时间戳的顺序来保证ID的唯一性,回拨使得新生成的ID在时间顺序上看起来比之前的ID更“旧”,从而可能被误认为是之前已经生成过的ID,破坏了ID的唯一性原则。 https://blog.csdn.net/2303_80933038/article/details/149261870?fromshare=blogdetail&sharetype=blogdetail&sharerId=149261870&sharerefer=PC&sharesource=2303_80933038&sharefrom=from_link

分片策略

表示逻辑表要如何分配到真实库和真实表当中,分为分库策略分表策略两个部分。分片策略由分片键和分片算法组成分片键是进行数据水平拆分的关键字段。分片算法则表示根据分片键如何寻找对应的真实库和真实表。示例当中对cid字段取模,就是一种简单的分片算法。

如果ShardingSphere匹配不到合适的分片策略,那就只能进行全分片路由,这是效率最差的一种实现方式。甚至还会抛异常

分配策略(如何分配到真实库和真实表)

=分库策略+分片策略

=分库策略+分片键(关键字段)+分片算法

 2、垂直分片和水平分片

 当我们设计分库分表方案时,通常有两种拆分数据的维度。

一是按照业务划分的维度,将不同的表拆分到不同的库当中。这样可以减少每个数据库的数据量以及客户端的连接数,提高查询效率。这种方案称为垂直分库。

二是按照数据分布的维度,将原本存在同一张表当中的数据,拆分到多张子表当中。每个子表只存储一部分数据。这样可以介绍每一张表的数据量,提升查询效率。这种方案称为水平分表。

  • 业务(垂直):多加数据库放表
  • 数据(水平):多加子表放表

三、ShardingJDBC常见数据分片策略实战 

通常我们讲的分库分表,主要是指水平分片,因为这样才能减少数据量,从根本上解决数据量过大带来的存储和查询的问题。但是,这也并不意味着垂直分片方案就不重要。

四种策略:

  1. standard(单一分片的标准查询方式)
  2. complex(多个分片的组合策略)
  3. CLASS_BASED(扩展分片策略)
  4. hint(与SQL无关的方式进行强制分片)

1、INLINE简单分片

INLINE简单分片主要用来处理针对分片建的 = 和 in 这样的查询操作。在这些操作中,可以拿到分片键的精确值。例如对下面这样的操作:

    /**
     * 针对分片键进行精确查询,都可以使用表达式控制
     */
    @Test
    public void queryCourse() {
        QueryWrapper<Course> wrapper = new QueryWrapper<Course>();
        wrapper.eq("cid",924770131651854337L);
//        wrapper.in("cid",901136075815124993L,901136075903205377L,901136075966119937L,5L);
        //带上排序条件不影响分片逻辑
//        wrapper.orderByDesc("user_id");
        List<Course> courses = courseMapper.selectList(wrapper);
        courses.forEach(course -> System.out.println(course));
    }

对于这样的操作,拿到分片键的精确值后,都可以通过表达式计算出可能的真实库以及真实表。而ShardingJDBC就会将逻辑SQL转化成对应的真实SQL,并路由到真实库中去执行。

(问题1)ShardingJDBC关注的是过滤数据的关键查询条件中是否包含了分片键,而并只是简单关注附加的条件。 例如在SQL语句后面加上order by user_id,并不会影响ShardingJDBC的处理过程。而如果查询条件中不包含分片建,那么ShardingJDBC就只能根据actual-nodes,到所有的真实表和真实库中查询。这也就是全分片路由。

对于全分片路由,ShardingJDBC做了一定的优化。比如通过Union将同一库的多条语句结合起来,这样可以减少与数据库的交互次数。

[ INFO] ShardingSphere-SQL             :Logic SQL: SELECT  cid,cname,user_id,cstatus  FROM course
[ INFO] ShardingSphere-SQL             :Actual SQL: m0 ::: SELECT  cid,cname,user_id,cstatus  FROM course_1 UNION ALL SELECT  cid,cname,user_id,cstatus  FROM course_2
[ INFO] ShardingSphere-SQL             :Actual SQL: m1 ::: SELECT  cid,cname,user_id,cstatus  FROM course_1 UNION ALL SELECT  cid,cname,user_id,cstatus  FROM course_2

但是,在真实项目中,这种全分片路由是一定要尽力避免的。因为在真实项目中,你要面对的,就不是示例中少数的几个分片了,通常都是几十个甚至上百个分片。在这样大数据情况下,进行全分片路由,效率是非常低的。

(问题2)ShardingJDBC其实只负责改写以及路由SQL,至于有没有数据,他就无法关心了。例如,在之前的示例中,我们提出了将分表规则改写为course_$->{((cid+1)%4).intdiv(2)+1},就能在cid连续递增的情况下,保证数据均匀分布。那么在此时,你就可以尝试去修改一下分表规则,然后查询cid in (1L,2L,3L,4L),然后去分析一下ShardingJDBC会如何执行。

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无法根据配置的表达式计算出可能的分片情况,在执行时就会抛出一个异常

报错信息明确提到需要添加一个allow-range-query-with-inline-sharding参数。这时,就需要给course_tbl_alg算法添加这个参数。

# 允许在inline策略中使用范围查询。
spring.shardingsphere.rules.sharding.sharding-algorithms.course_tbl_alg.props.allow-range-query-with-inline-sharding=true

 但观察一下Actual SQL的执行方式,你会发现这时SQL还是按照全路由的方式执行的,后续解答

3、COMPLEX_INLINE复杂分片

需要针对多个属性进行组合查询

   @Test
    public void queryCourseComplexSimple(){
        QueryWrapper<Course> wrapper = new QueryWrapper<Course>();
        wrapper.orderByDesc("user_id");
        wrapper.in("cid",851198095084486657L,851198095139012609L);
        wrapper.eq("user_id",1001L);
        List<Course> course = courseMapper.selectList(wrapper);
        //select * from couse where cid in (xxx) and user_id =xxx
        System.out.println(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+1)%2+1}

4、CLASS_BASED自定义分片

就算是有一些数据根本和数据库的数据差得不是一点半点,根本就没必要分片去查浪费性能

所以我们需要定制

配置文件:

# 使用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
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;
    }
}

这时,再去执行查询方法,就会得到这样的异常信息。

在我们当前设定的业务场景下,这其实不是出问题了,而是对数据库性能的保护。

这里抛出的是SQLException,因为ShardingSphere实际上是在模拟成一个独立的虚拟数据库,在这个虚拟数据库中执行出现的异常,也都作为SQL异常抛出来。

5、HINT_INLINE强制分片

查特定数据

 需要查询所有cid为奇数的课程信息。这要怎么查呢?

SQL语句(全分片查询):

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表指定分表策略  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}

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

    @Test
    public void queryCourseByHint(){
        //强制只查course_1表
        HintManager hintManager = HintManager.getInstance();
        // 强制查course_1表
        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 }
    }

这样就可以让SQL语句只查询course_1表,在当前场景下,也就相当于是实现了只查cid为奇数的需求。

6、常用分片算法总结

在之前的示例中就介绍了ShardingSphere提供的MOD、HASH-MOD这样的简单内置分片策略,standard、complex、hint三种典型的分片策略以及CLASS_BASED这种扩展分片策略的方法。为什么要有这么多的分片策略,其实就是以为分库分表面临的业务场景其实是很复杂的。

即便是ShardingSphere,也无法真的像MySQL、Oracle这样的数据库产品一样,完美的兼容所有的SQL语句。

因此,一旦开始决定用分库分表,那么后续业务中的每一个SQL语句就都需要结合分片策略进行思考,不能像操作真正数据库那样随心所欲了。


网站公告

今日签到

点亮在社区的每一天
去签到