Loading

ShardingJDBC

分库分表概念

  • 垂直角度(表结构不一样,大结构分多个小结构)
    • 垂直分表: 将一个表字段拆分多个表,每个表存储部分字段
      • 好处: 避免IO时锁表的次数,分离热点字段和非热点字段,避免大字段IO导致性能下降
      • 原则:业务经常组合查询的字段一个表;不常用字段一个表;text、blob类型字段作为附属表
    • 垂直分库:根据业务将表分类,放到不同的数据库服务器上
      • 好处:避免表之间竞争同个物理机的资源,比如CPU/内存/硬盘/网络IO
      • 原则:根据业务相关性进行划分,领域模型,微服务划分一般就是垂直分库
  • 水平角度(表结构一样,内容分散)
    • 水平分库:把同个表的数据按照一定规则分到不同的数据库中,数据库在不同的服务器上
      • 好处: 多个数据库,降低了系统的IO和CPU压力
      • 原则
        • 选择合适的分片键和分片策略,和业务场景配合
        • 避免数据热点和访问不均衡、避免二次扩容难度大
    • 水平分表:同个数据库内,把一个表的数据按照一定规则拆分到多个表中,对数据进行拆分,不影响表结构
      • 单个表的数据量少了,业务SQL执行效率高,降低了系统的IO和CPU压力
      • 原则
        • 选择合适的分片键和分片策略,和业务场景配合
        • 避免数据热点和访问不均衡、避免二次扩容难度大

水平分库分表常见策略

Range

方案一:自增id,根据ID范围进行分表(左闭右开)

  • 规则案例
    • 1~1,000,000 是 table_1
    • 1,000,000 ~2,000,000 是 table_2
    • 2,000,000~3,000,000 是 table_3
    • ...更多
  • 优点
    • id是自增长,可以无限增长
    • 扩容不用迁移数据,容易理解和维护
  • 缺点
    • 大部分读和写都访会问新的数据,有IO瓶颈,整体资源利用率低
    • 数据倾斜严重,热点数据过于集中,部分节点有瓶颈
  • image-20230101114931903

Hash

方案二:hash取模(Hash分库分表是最普遍的方案)

  • 案例规则

    • 用户ID是整数型的,要分2库,每个库表数量4表,一共8张表
    • 用户ID取模后,值是0到7的要平均分配到每张表
    A库ID = userId % 库数量 2 
    
    表ID = userId / 库数量 2 % 表数量4
    
    • 例子
    userId id % 2 (库-取余) id /2 % 4 (表)
    1 1 0
    2 0 1
    3 1 1
    4 0 2
    5 1 2
    6 0 3
    7 1 3
    8 0 0
    9 1 0
  • 优点

    • 保证数据较均匀的分散落在不同的库、表中,可以有效的避免热点数据集中问题,
  • 缺点

    • 扩容不是很方便,需要数据迁移
  • image-20230101115120063

SharingSphere

ShardingJdbc术语

  • 数据节点Node

    • 数据分片的最小单元,由数据源名称和数据表组成
    • 比如:ds_0.product_order_0
  • 真实表

    • 在分片的数据库中真实存在的物理表
    • 比如订单表 product_order_0、product_order_1、product_order_2
  • 逻辑表

    • 水平拆分的数据库(表)的相同逻辑和数据结构表的总称
    • 比如订单表 product_order_0、product_order_1、product_order_2,逻辑表就是product_order
  • 绑定表

    • 指分片规则一致的主表和子表
    • 比如product_order表和product_order_item表,均按照order_id分片,则此两张表互为绑定表关系
    • 绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升

    image-20211125120921994

  • 广播表

    • 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
    • 适用于数据量不大且需要与海量数据的表进行关联查询的场景
    • 例如:字典表、配置表

ShardingJdbc算法

  • 数据库表分片(水平库、表)

    • 包含分片键和分片策略
  • 分片键 (PartitionKey)

    • 用于分片的数据库字段,是将数据库(表)水平拆分的关键字段
    • 比如prouduct_order订单表,根据订单号 out_trade_no做哈希取模,则out_trade_no是分片键
    • 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片
  • 分片策略

    image-20211125140127962

    • 行表达式分片策略 InlineShardingStrategy(必备

      • 只支持【单分片键】使用Groovy的表达式,提供对SQL语句中的 =和IN 的分片操作支持

      • 可以通过简单的配置使用,无需自定义分片算法,从而避免繁琐的Java代码开发

      • prouduct_order_$->{user_id % 8}` 表示订单表根据user_id模8,而分成8张表,表名称为`prouduct_order_0`到`prouduct_order_7
        
    • 标准分片策略StandardShardingStrategy(需了解)

      • 只支持【单分片键】,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法
      • PreciseShardingAlgorithm 精准分片 是必选的,用于处理=和IN的分片
      • RangeShardingAlgorithm 范围分配 是可选的,用于处理BETWEEN AND分片
      • 如果不配置RangeShardingAlgorithm,如果SQL中用了BETWEEN AND语法,则将按照全库路由处理,性能下降
    • 复合分片策略ComplexShardingStrategy(需了解)

      • 支持【多分片键】,多分片键之间的关系复杂,由开发者自己实现,提供最大的灵活度
      • 提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持
    • Hint分片策略HintShardingStrategy(需了解)

      • 这种分片策略无需配置分片健,分片健值也不再从 SQL中解析,外部手动指定分片健或分片库,让 SQL在指定的分库、分表中执行
      • 用于处理使用Hint行分片的场景,通过Hint而非SQL解析的方式分片的策略
      • Hint策略会绕过SQL解析的,对于这些比较复杂的需要分片的查询,Hint分片策略性能可能会更好
    • 不分片策略 NoneShardingStrategy(需了解)

      • 不分片的策略。

SpringBoot整合

框架版本说明

<properties>
     <!--JDK版本,如果是jdk8则这里是 1.8-->
     <java.version>11</java.version>
     <maven.compiler.source>11</maven.compiler.source>
     <maven.compiler.target>11</maven.compiler.target>
     <spring.boot.version>2.5.5</spring.boot.version>
     <mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version>
     <lombok.version>1.18.16</lombok.version>
     <sharding-jdbc.version>4.1.1</sharding-jdbc.version>
     <junit.version>4.12</junit.version>
     <druid.version>1.1.16</druid.version>
     <!--跳过单元测试-->
     <skipTests>true</skipTests>
</properties>

maven pom文件配置

<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>${spring.boot.version}</version>
    </dependency>

    <!--<dependency>-->
    <!--<groupId>org.springframework.boot</groupId>-->
    <!--<artifactId>spring-boot-test</artifactId>-->
    <!--</dependency>-->

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>${spring.boot.version}</version>
        <scope>test</scope>
    </dependency>


    <!--mybatis plus和springboot整合-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>${mybatisplus.boot.starter.version}</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <!--<scope>provided</scope>-->
    </dependency>

    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>${sharding-jdbc.version}</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring.boot.version}</version>
            <configuration>
                <fork>true</fork>
                <addResources>true</addResources>
            </configuration>
        </plugin>

    </plugins>
</build>

多数据库配置

spring.application.name=xdclass-sharding-jdbc
server.port=8080

logging.level.root=INFO

# 打印执行的数据库以及语句
spring.shardingsphere.props.sql.show=true

# 数据源 db0
spring.shardingsphere.datasource.names=ds0,ds1

# 第一个数据库
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://ip:33306/xdclass_shop_order_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root

# 第二个数据库
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://ip:33306/xdclass_shop_order_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

水平分表配置

# 指定product_order表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},
# 但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}

# 多表
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}
# 多库多表
#spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}

# 指定product_order表的分片策略,分片策略包括【分片键和分片算法】
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{id % 2}
public class ProductOrderDO {

    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    private String outTradeNo;

    private String state;

    private Date createTime;

    private Double payAmount;

    private String nickname;

    private Long userId;
}
  • 问题
    • 主键重复问题(两张表都会生成数据,ID自增会产生同样的ID)

分布式ID生成器

## workerId配置
spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1
## id生成策略
spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE

注意:

  • 注释掉实体类中ID增长注解
  • workerId需要配置Id生成策略,不然会报错

广播表

  • 什么是广播表
    • 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
    • 适用于数据量不大且需要与海量数据的表进行关联查询的场景
    • 例如:字典表、配置表
  • 注意点:
    • 分库分表中间件,对应的数据库字段,不能是sql的关键字,否则容易出问题,且报错不明显
  • 配置实战
#配置广播表
spring.shardingsphere.sharding.broadcast-tables=ad_config
spring.shardingsphere.sharding.tables.ad_config.key-generator.column=id
spring.shardingsphere.sharding.tables.ad_config.key-generator.type=SNOWFLAKE

水平分库分表配置

# 多数据源 db0
spring.shardingsphere.datasource.names=ds0,ds1

#配置分库规则
spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.algorithm-expression=ds$->{user_id % 2}

# 分表策略修改为多数据库
#spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}

绑定表

  • 绑定表
    • 指分片规则一致的主表和子表
    • 比如product_order表和product_order_item表,均按照order_id分片,则此两张表互为绑定表关系
    • 绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升

image-20211125120921994

  • 表讲解
CREATE TABLE `product_order_item_0` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `product_order_id` bigint DEFAULT NULL COMMENT '订单号',
  `product_id` bigint DEFAULT NULL COMMENT '产品id',
  `product_name` varchar(128) DEFAULT NULL COMMENT '商品名称',
  `buy_num` int DEFAULT NULL COMMENT '购买数量',
  `user_id` bigint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  • 配置实战
#分库策略 默认
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression = ds$->{user_id % 2}


# 指定product_order_item表的数据分布情况
spring.shardingsphere.sharding.tables.product_order_item.actual-data-nodes=ds$->{0..1}.product_order_item_$->{0..1}
spring.shardingsphere.sharding.tables.product_order_item.table-strategy.inline.sharding-column=product_order_id
spring.shardingsphere.sharding.tables.product_order_item.table-strategy.inline.algorithm-expression=product_order_item_$->{product_order_id % 2}

#绑定表
spring.shardingsphere.sharding.binding‐tables[0] = product_order,product_order_item
  • 未使用绑定表的SQL

image-20230101154514865

  • 使用绑定表后的SQL

image-20230101154548540

多种分片策略

精准分片

  • StandardShardingStrategy

    • 只支持【单分片键】,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法
    • PreciseShardingAlgorithm 精准分片 是必选的,用于处理=和IN的分片
    • RangeShardingAlgorithm 范围分片 是可选的,用于处理BETWEEN AND分片
    • 如果不配置RangeShardingAlgorithm,如果SQL中用了BETWEEN AND语法,则将按照全库路由处理,性能下降
  • 代码

  • public class CustomTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    
        /**
         *
         * @param dataSourceNames 数据源集合
         *                      在分库时值为所有分片库的集合 databaseNames
         *                      分表时为对应分片库中所有分片表的集合 tablesNames
         *
         * @param shardingValue  分片属性,包括
         *                                  logicTableName 为逻辑表,
         *                                  columnName 分片健(字段),
         *                                  value 为从 SQL 中解析出的分片健的值
         * @return
         */
        @Override
        public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> shardingValue) {
            for (String databaseName : dataSourceNames) {
    
                String value = shardingValue.getValue() % dataSourceNames.size() + "";
                //value是0,则进入0库表,1则进入1库表
                if (databaseName.endsWith(value)) {
                    return databaseName;
                }
    
            }
            throw new IllegalArgumentException();
        }
    }
    
    
  • 新增订单记录

Random random = new Random();
for(int i=0;i<5;i++){
    ProductOrder productOrder = new ProductOrder();
    productOrder.setCreateTime(new Date());
    productOrder.setNickname("000二当家i="+i);
    productOrder.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
    productOrder.setPayAmount(100.00);
    productOrder.setState("PAY");
    int value = random.nextInt(100);
    productOrder.setUserId(Long.valueOf(value));
    productOrderMapper.insert(productOrder);
}
  • 配置文件(注释其他)
# 指定product_order表的数据分布情况,配置数据节点,在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}

#指定精准分片算法(水平分表)
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.precise-algorithm-class-name=net.xdclass.strategy.CustomTablePreciseShardingAlgorithm

精准分片

  • 代码
public class CustomDBPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {

    /**
     *
     * @param dataSourceNames 数据源集合
     *                      在分库时值为所有分片库的集合 databaseNames
     *                      分表时为对应分片库中所有分片表的集合 tablesNames
     *
     * @param shardingValue  分片属性,包括
     *                                  logicTableName 为逻辑表,
     *                                  columnName 分片健(字段),
     *                                  value 为从 SQL 中解析出的分片健的值
     * @return
     */
    @Override
    public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> shardingValue) {
        for (String databaseName : dataSourceNames) {

            String value = shardingValue.getValue() % dataSourceNames.size() + "";
            //value是0,则进入0库表,1则进入1库表
            if (databaseName.endsWith(value)) {
                return databaseName;
            }

        }
        throw new IllegalArgumentException();
    }
}
  • 新增订单记录
Random random = new Random();
for(int i=0;i<5;i++){
    ProductOrder productOrder = new ProductOrder();
    productOrder.setCreateTime(new Date());
    productOrder.setNickname("000二当家i="+i);
    productOrder.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
    productOrder.setPayAmount(100.00);
    productOrder.setState("PAY");
    int value = random.nextInt(100);
    productOrder.setUserId(Long.valueOf(value));
    productOrderMapper.insert(productOrder);
}
  • 分库(自定义分库和分表算法的实现基本是一样的
#指定全部数据节点,水平分库分表
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}

# 分库分片健
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=user_id
# 分库分片算法
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.precise-algorithm-class-name=net.xdclass.strategy.CustomDBPreciseShardingAlgorithm

范围分片

  • RangeShardingAlgorithm 范围分片
    • 用于处理BETWEEN AND语法,没配置的话会报错 Cannot find range sharding strategy in sharding rule.
    • 主要是会根据 SQL中给出的分片健值范围值处理分库、分表逻辑
  • 代码
public class CustomRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
    @Override
    public Collection<String> doSharding(Collection<String> dataSourceNames, RangeShardingValue<Long> rangeShardingValue) {

        Set<String> result = new LinkedHashSet<>();
        // between 起始值
        Long lower = rangeShardingValue.getValueRange().lowerEndpoint();
        // between 结束值
        Long upper = rangeShardingValue.getValueRange().upperEndpoint();

        // 循环范围计算分库逻辑
        for (long i = lower; i <= upper; i++) {
            for (String databaseName : dataSourceNames) {
                if (databaseName.endsWith(i % dataSourceNames.size() + "")) {
                    result.add(databaseName);
                }
            }
        }
        return result;
    }
}
  • 配置
#精准水平分表下,增加一个范围分片
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.range-algorithm-class-name=net.xdclass.strategy.CustomRangeShardingAlgorithm
  • 测试代码
productOrderMapper.selectList(new QueryWrapper<ProductOrder>().between("id",1L,1L));

productOrderMapper.selectList(new QueryWrapper<ProductOrder>().between("id",1L,3L));

复合分片

  • 复合分片算法ComplexShardingStrategy (了解即可)
    • 提供对SQL语句中的=, IN和BETWEEN AND的分片操作,支持【多分片键】
    • 由于多分片键之间的关系复杂,Sharding-JDBC并未做过多的封装
    • 而是直接将分片键值组合以及分片操作符交于算法接口,全部由应用开发者实现,提供最大的灵活度
  • 编码
public class CustomComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
    /**
     * @param dataSourceNames 数据源集合
     *                        在分库时值为所有分片库的集合 databaseNames:比如表:product_order_0/product_order_1、库ds0/ds1 等
     *                        分表时为对应分片库中所有分片表的集合 tablesNames
     * @param shardingValue   分片属性,包括
     *                        logicTableName 为逻辑表,
     *                        columnNameAndShardingValuesMap 存储多个分片健,包括key-value
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection<String> dataSourceNames, ComplexKeysShardingValue<Long> complexKeysShardingValue) {

        // 得到每个分片健对应的值
        Collection<Long> orderIdValues = this.getShardingValue(complexKeysShardingValue, "id");
        Collection<Long> userIdValues = this.getShardingValue(complexKeysShardingValue, "user_id");

        List<String> shardingSuffix = new ArrayList<>();
        // 对两个分片健取模的方式
        for (Long userId : userIdValues) {
            for (Long orderId : orderIdValues) {
                String suffix = userId % 2 + "_" + orderId % 2;
                for (String databaseName : dataSourceNames) {
                    if (databaseName.endsWith(suffix)) {
                        shardingSuffix.add(databaseName);
                    }
                }
            }
        }
        return shardingSuffix;
    }

    /**
     * shardingValue  分片属性,包括
     * logicTableName 为逻辑表,
     * columnNameAndShardingValuesMap 存储多个分片健 包括key-value
     * key:分片key,id和user_id
     * value:分片value,66和99
     *
     * @return shardingValues 集合
     */
    private Collection<Long> getShardingValue(ComplexKeysShardingValue<Long> shardingValues, final String key) {
        Collection<Long> valueSet = new ArrayList<>();
        Map<String, Collection<Long>> columnNameAndShardingValuesMap = shardingValues.getColumnNameAndShardingValuesMap();

        if (columnNameAndShardingValuesMap.containsKey(key)) {
            valueSet.addAll(columnNameAndShardingValuesMap.get(key));
        }
        return valueSet;
    }
}
  • 配置(分表)
    • 记得注释其他策略,否则报错 Only allowed 0 or 1 sharding strategy configuration
## 复合分片算法,order_id,user_id 同时作为分片健
spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.sharding-columns=user_id,id
spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.algorithm-class-name=net.xdclass.strategy.CustomComplexKeysShardingAlgorithm

  • 测试代码
productOrderMapper.selectList(new QueryWrapper<ProductOrder>().eq("id",66L).eq("user_id",99L));

Hint分片

  • Hint分片策略HintShardingStrategy

    • hint的中文意思:提示、暗示
    • 这种分片策略无需配置文件进行配置分片健,分片健值也不再从 SQL中解析,外部手动指定分片健或分片库,让 SQL在指定的分库、分表中执行
    • 通过Hint代码指定的方式而非SQL解析的方式分片的策略
    • Hint策略会绕过SQL解析的,对于这些比较复杂的需要分片的查询,Hint分片策略性能可能会更好
    • 可以指定sql去某个库某个表进行执行
  • 编码(自定义完算法只实现了一部分,需要在调用 SQL 前通过 HintManager 指定分库、分表信息)

    • 分库

    • public class CustomDBHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
          /**
           *
           * @param dataSourceNames 数据源集合
           *                      在分库时值为所有分片库的集合 databaseNames
           *                      分表时为对应分片库中所有分片表的集合 tablesNames
           *
           * @param hintShardingValue  分片属性,包括
           *                                  logicTableName 为逻辑表,
           *                                  columnName 分片健(字段),hit策略此处为空 ""
           *
           *                                  value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断
           *                                   HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过
           *                                   hintManager.addTableShardingValue("product_order", 1)参数进行指定
           * @return
           */
          @Override
          public Collection<String> doSharding(Collection<String> dataSourceNames,
                                               HintShardingValue<Long> hintShardingValue) {
      
              Collection<String> result = new ArrayList<>();
              for (String tableName : dataSourceNames) {
      
                  for (Long shardingValue : hintShardingValue.getValues()) {
      
                      if (tableName.endsWith(String.valueOf(shardingValue % dataSourceNames.size()))) {
                          result.add(tableName);
                      }
                  }
      
              }
              return result;
          }
      }
      
    • 分表

    • public class CustomTableHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
          /**
           *
           * @param dataSourceNames 数据源集合
           *                      在分库时值为所有分片库的集合 databaseNames
           *                      分表时为对应分片库中所有分片表的集合 tablesNames
           *
           * @param hintShardingValue  分片属性,包括
           *                                  logicTableName 为逻辑表,
           *                                  columnName 分片健(字段),hit策略此处为空 ""
           *
           *                                  value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断
           *                                   HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过
           *                                   hintManager.addTableShardingValue("product_order", 1)参数进行指定
           * @return
           */
          @Override
          public Collection<String> doSharding(Collection<String> dataSourceNames,
                                               HintShardingValue<Long> hintShardingValue) {
      
              Collection<String> result = new ArrayList<>();
              for (String tableName : dataSourceNames) {
      
                  for (Long shardingValue : hintShardingValue.getValues()) {
      
                      if (tableName.endsWith(String.valueOf(shardingValue % dataSourceNames.size()))) {
                          result.add(tableName);
                      }
                  }
      
              }
              return result;
          }
      }
      
  • 配置(配置多个数据源ds)

# Hint分片算法
spring.shardingsphere.sharding.tables.product_order.table-strategy.hint.algorithm-class-name=net.xdclass.strategy.CustomTableHintShardingAlgorithm
spring.shardingsphere.sharding.tables.product_order.database-strategy.hint.algorithm-class-name=net.xdclass.strategy.CustomDBHintShardingAlgorithm

  • 编码测试
@Test
    public void testHint() {
        // 清除掉历史的规则
        HintManager.clear();
        //Hint分片策略必须要使用 HintManager工具类
        HintManager hintManager = HintManager.getInstance();
        // 设置库的分片健,value用于库分片取模,
        hintManager.addDatabaseShardingValue("product_order",3L);

        // 设置表的分片健,value用于表分片取模,
        //hintManager.addTableShardingValue("product_order", 7L);
        hintManager.addTableShardingValue("product_order", 8L);

        // 如果在读写分离数据库中,Hint 可以强制读主库(主从复制存在一定延时,但在业务场景中,可能更需要保证数据的实时性)
        //hintManager.setMasterRouteOnly();

        //对应的value只做查询,不做sql解析
        productOrderMapper.selectList(new QueryWrapper<ProductOrder>().eq("id", 66L));
    }
  • 之前没用partitionKey会触发全库表路由,发出很多不相干的SQL. 使用Hint方式是可以避免这个问题
posted @ 2023-01-01 16:17  yonugleesin  阅读(387)  评论(0编辑  收藏  举报