Spring boot 集成 Sharding JDBC 实现分库分表

584 阅读3分钟

本文主要通过项目代码讲述Springboot如何集成ShardingJDBC实现分库分表,相关概念问题会在其他博客中记录。

1. 项目搭建

直接通过IDEA创建Spring boot项目即可。

2. 依赖引入

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0-alpha</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>
        </dependency>
    </dependencies>

3. 业务代码

该项目中的相关业务代码通过mybatis plus直接生成,比较简单,就不过多描述。具体代码会在文末给出地址。

测试的时候自行查看相关接口路径。

image-20220106130214215

4. 分库分配配置

在spring boot项目中,我们不需要像使用API一样繁琐,分库分表我们全部以配置的形式来实现。

相关的配置项可以参考官网:(根据自身需要查看YML或者properties配置项)

shardingsphere.apache.org/document/cu…

image-20220106124115658

我们通过sharding jdbc 实现分库分表可以通过其提供的内置分片算法实现,也可以根据自身需要实现自定义算法。

shardingsphere.apache.org/document/cu…

这边由于我们需要测试多个分片算法,所以我们创建多个版本的配置,通过spring.profiles.active选择对应的配置。

image-20220106124323132

4.1 行表达式分片算法

我们首先来测试下行表达式分片算法,这是一个内置的标准分片算法。

使用 Groovy 的表达式,提供对 SQL 语句中的 =IN 的分片操作支持,只支持单分片键。 对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的 Java 代码开发,如: t_user_$->{u_id % 8} 表示 t_user 表根据 u_id 模 8,而分成 8 张表,表名称为 t_user_0t_user_7。 详情请参见行表达式

类型:INLINE

可配置属性:

属性名称数据类型说明默认值
algorithm-expressionString分片算法的行表达式
allow-range-query-with-inline-sharding (?)boolean是否允许范围查询。注意:范围查询会无视分片策略,进行全路由false

相应的配置,我相应大家应该很容易就能看明白。首先创建两个数据源,然后创建对应的分库分表规则,定义主键算法等。所有的配置项我们都可以在官网中获取。

server.port=8080
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html
spring.shardingsphere.datasource.names=ds-0,ds-1
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver
# 数据源
spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://localhost:3306/shard01?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.ds-1.username=root
spring.shardingsphere.datasource.ds-1.password=123456
spring.shardingsphere.datasource.ds-1.jdbc-url=jdbc:mysql://localhost:3306/shard02?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
# 针对分库的规则配置
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-algorithm-name=database-inline
# 逻辑表的绑定
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=ds-$->{0..1}.t_order_$->{0..1}
# 针对分表的策略
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=t-order-inline
# 单个绑定表
#spring.shardingsphere.rules.sharding.binding-tables=t_order,t_order_detail
## 如果有多个绑定表
#spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_detail
#spring.shardingsphere.rules.sharding.binding-tables[1]=t_order,t_order_detail
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=snowflake
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds-$->{user_id % 2}
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.props.algorithm-expression=t_order_$->{order_id % 2}
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-item-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-item-inline.props.algorithm-expression=t_order_item_$->{order_id % 2}
spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123

配置项设置好后,我们就可以直接启动项目测试了。

需要注意的是,我们提前在数据库中创建好对应数据库,表会自动生成。

从下图结果可以发现数据按照分片规则存进了对应的表中。

image-20220106125207982

image-20220106125214511

4.2 基于分片容量的范围分片算法

类型:VOLUME_RANGE

可配置属性:

属性名称数据类型说明
range-lowerlong范围下界,超过边界的数据会报错
range-upperlong范围上界,超过边界的数据会报错
sharding-volumelong分片容量
server.port=8081
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html

spring.shardingsphere.datasource.names=ds-0
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://localhost:3306/shard01?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8

spring.shardingsphere.rules.sharding.tables.t_order_volume_range.actual-data-nodes=ds-0.t_order_volume_range_$->{0..2}
spring.shardingsphere.rules.sharding.tables.t_order_volume_range.table-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.tables.t_order_volume_range.table-strategy.standard.sharding-algorithm-name=t-order-volume-range

spring.shardingsphere.rules.sharding.tables.t_order_volume_range.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order_volume_range.key-generate-strategy.key-generator-name=snowflake

# 可以在分布式配置中心动态修改相关值
# 基于范围的分片策略 区间分布 如下是每200一个区间;600为最大容量;第一个分片容量为200
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-volume-range.type=VOLUME_RANGE
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-volume-range.props.range-lower=200
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-volume-range.props.range-upper=600
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-volume-range.props.sharding-volume=200

spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123

从结果可以看出数据按照每200一个区间将数据放到对应的表中。

image-20220106125655878

image-20220106125729300

4.3 基于分片边界的范围分片算法

类型:BOUNDARY_RANGE

可配置属性:

属性名称数据类型说明
sharding-rangesString分片的范围边界,多个范围边界以逗号分隔

该算法按照范围边界进行数据的划分,比如我们下面定义的1000,20000,300000;

会将数据分成如下四个区间:

  • 0~1000
  • 1000~20000
  • 20000~300000
  • 300000~无穷大
server.port=8080
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html

spring.shardingsphere.datasource.names=ds-0
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://localhost:3306/shard01?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8

spring.shardingsphere.rules.sharding.tables.t_order_boundary_range.actual-data-nodes=ds-0.t_order_boundary_range_$->{0..3}
spring.shardingsphere.rules.sharding.tables.t_order_boundary_range.table-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.tables.t_order_boundary_range.table-strategy.standard.sharding-algorithm-name=t-order-boundary-range

spring.shardingsphere.rules.sharding.tables.t_order_boundary_range.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order_boundary_range.key-generate-strategy.key-generator-name=snowflake
# 基于分片边界的范围分片算法
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-boundary-range.type=BOUNDARY_RANGE
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-boundary-range.props.sharding-ranges=1000,20000,300000

spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123

image-20220106130022531

4.4 自动时间段分片算法

类型:AUTO_INTERVAL

可配置属性:

属性名称数据类型说明
datetime-lowerString分片的起始时间范围,时间戳格式:yyyy-MM-dd HH:mm:ss
datetime-upperString分片的结束时间范围,时间戳格式:yyyy-MM-dd HH:mm:ss
sharding-secondslong单一分片所能承载的最大时间,单位:秒
server.port=8080
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html

spring.shardingsphere.datasource.names=ds-0
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://localhost:3306/shard01?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8

spring.shardingsphere.rules.sharding.tables.t_order_interval.actual-data-nodes=ds-0.t_order_interval_$->{0..12}
spring.shardingsphere.rules.sharding.tables.t_order_interval.table-strategy.standard.sharding-column=create_time
spring.shardingsphere.rules.sharding.tables.t_order_interval.table-strategy.standard.sharding-algorithm-name=t-order-auto-interval

spring.shardingsphere.rules.sharding.tables.t_order_interval.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order_interval.key-generate-strategy.key-generator-name=snowflake

spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-auto-interval.type=AUTO_INTERVAL
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-auto-interval.props.datetime-lower=2010-01-01 23:59:59
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-auto-interval.props.datetime-upper=2023-01-01 23:59:59
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-auto-interval.props.sharding-seconds=31536000

spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123

image-20220106130257538

4.5 自定义分片算法

如果内置算法不能满足你的需求,我们可以自定义分片算法。

我们需要通过实现StandardShardingAlgorithm接口创建我们的自定义算法,处理好分片算法后,还需要指定算法类型,方便再配置文件中查询。

public class StandardModTableShardAlgorithm implements StandardShardingAlgorithm<Long> {

    private Properties props = new Properties();

    /**
     * 用于处理=和IN的分片。
     *
     * @param collection           表示目标分片的集合
     * @param preciseShardingValue 逻辑表相关信息
     * @return Mod
     */
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
        for (String name : collection) {
            //根据order_id的值进行取模,得到一个目标值
            //Order_id%4=3
            //name.endsWith,  "order_3".endWith("")
            if (name.endsWith(String.valueOf(preciseShardingValue.getValue() % 4))) {
                return name;
            }
        }
        throw new UnsupportedOperationException();
    }

    /**
     * 用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理
     *
     * @param collection
     * @param rangeShardingValue
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
        Collection<String> result = new LinkedHashSet<>(collection.size());
        for (Long i = rangeShardingValue.getValueRange().lowerEndpoint(); i <= rangeShardingValue.getValueRange().upperEndpoint(); i++) {
            for (String name : collection) {
                if (name.endsWith(String.valueOf(i % 4))) {
                    result.add(name);
                }
            }
        }
        return result;
    }

    /**
     * 初始化对象的时候调用的方法
     */
    @Override
    public void init() {

    }

    /**
     * 对应分片算法(sharding-algorithms)的类型
     *
     * @return
     */
    @Override
    public String getType() {
        return "STANDARD_MOD";
    }


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

    /**
     * 获取分片相关属性
     *
     * @param properties
     */
    @Override
    public void setProps(Properties properties) {
        this.props = properties;
    }
}

在配置文件中需要指定我们刚刚实现类中的类型以及写入全路径。

server.port=8080
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html

spring.shardingsphere.datasource.names=ds-0
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://localhost:3306/shard01?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8

spring.shardingsphere.rules.sharding.tables.t_order_standard.actual-data-nodes=ds-0.t_order_standard_$->{0..3}
spring.shardingsphere.rules.sharding.tables.t_order_standard.table-strategy.standard.sharding-column=order_id
spring.shardingsphere.rules.sharding.tables.t_order_standard.table-strategy.standard.sharding-algorithm-name=standard-mod

spring.shardingsphere.rules.sharding.tables.t_order_standard.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order_standard.key-generate-strategy.key-generator-name=snowflake
# 自定义算法全路径
spring.shardingsphere.rules.sharding.sharding-algorithms.standard-mod.type=STANDARD_MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.standard-mod.props.algorithm-class-name=com.example.springbootexample.StandardModTableShardAlgorithm

spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123

这个时候如果我们运行的话,你会发现我们的项目运行失败了。这是咋回事呢?这是由于SPI动态拓展机制造成的,我们还需要进行额外的配置。(SPI机制会在另外的文章中说明)

我们需要创建这样一个文件,文件名是接口的全限定名。

然后内部则是我们刚刚定义的实现类路径。

image-20220106130846291

image-20220106130925316

创建好该配置后,我们的项目就可以成功启动了。

5. 项目地址

gitee.com/cl142974533…