Sharding-Jdbc核心流程+多种分片策略实战

DBC 404 0

一、Sharding-Jdbc实战之标准分片策略-精准分片算法《分表》

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

CustomTablePreciseShardingAlgorithm

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.util.Collection;

public class CustomTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {

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

        for(String datasourceName : dataSourceNames){

            String value = preciseShardingValue.getValue() % dataSourceNames.size() + "";
            //product_order_0
            if(datasourceName.endsWith(value)){
                return  datasourceName;
            }
        }
        return null;
    }
}

配置文件(注释其他)

点击查看完整内容

关键位置

#精准分片-水平分表
# 指定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 void testSaveProductOrder(){

        Random random = new Random();
        for(int i=0; i<20;i++){
            ProductOrderDO productOrderDO = new ProductOrderDO();
            productOrderDO.setCreateTime(new Date());
            productOrderDO.setNickname("大白菜i="+i);
            productOrderDO.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
            productOrderDO.setPayAmount(100.00);
            productOrderDO.setState("PAY");

            productOrderDO.setUserId( Long.valueOf(random.nextInt(50)) );

            productOrderMapper.insert(productOrderDO);

        }

    }

Sharding-Jdbc核心流程+多种分片策略实战插图
Sharding-Jdbc核心流程+多种分片策略实战插图2

温馨提示

可以看到效果还是不错的!

二、Sharding-Jdbc实战之标准分片策略-精准分片算法《分库分表》

配置文件

#精准分片-水平分表
# 指定product_order表的数据分布情况,配置数据节点,在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}


#指定精准分片算法(水平分库) 根据user_id分库
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

java代码

package net.xdclass.strategy;

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.util.Collection;



public class CustomDBPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {

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

        for(String datasourceName : dataSourceNames){

            String value = preciseShardingValue.getValue() % dataSourceNames.size() + "";
            //ds0、ds1
            if(datasourceName.endsWith(value)){
                return  datasourceName;
            }
        }
        return null;
    }
}

三、Sharding-Jdbc分片策略实战之标准分片策略-范围分片算法

  • RangeShardingAlgorithm 范围分片
    • 用于处理BETWEEN AND语法,没配置的话会报错 Cannot find range sharding strategy in sharding rule.
    • 主要是会根据 SQL中给出的分片健值范围值处理分库、分表逻辑
  • 编码
package net.xdclass.strategy;

import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;



public class CustomRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
    /**
     *
     * @param dataSourceNames 数据源集合
     *                      在分库时值为所有分片库的集合 databaseNames
     *                      分表时为对应分片库中所有分片表的集合 tablesNames
     *
     * @param shardingValue  分片属性,包括
     *                                  logicTableName 为逻辑表,
     *                                  columnName 分片健(字段),
     *                                  value 为从 SQL 中解析出的分片健的值
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection<String> dataSourceNames, RangeShardingValue<Long> shardingValue) {
        Set<String> result = new LinkedHashSet<>();

        //between 开始值
        Long lower  = shardingValue.getValueRange().lowerEndpoint();

        //between 结束值
        Long upper = shardingValue.getValueRange().upperEndpoint();

        for(long i=lower;i<=upper;i++){
            for(String datasource : dataSourceNames){
                String value = i % dataSourceNames.size() +"";
                if(datasource.endsWith(value)){
                    result.add(datasource);
                }
            }

        }
        return result;
    }
}

配置文件

#范围分片(水平分表)
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.range-algorithm-class-name=net.xdclass.strategy.CustomRangeShardingAlgorithm

简单测试

    @Test
    public void testBetween(){

        productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().between("id",1L,2L));
    }

Sharding-Jdbc核心流程+多种分片策略实战插图4

温馨提示

大功告成,没有问题啦!

四、Sharding-Jdbc分片策略实战之复合分片算法

  • 复合分片算法ComplexShardingStrategy (了解即可)
    • 提供对SQL语句中的=, IN和BETWEEN AND的分片操作,支持【多分片键】
    • 由于多分片键之间的关系复杂,Sharding-JDBC并未做过多的封装
    • 而是直接将分片键值组合以及分片操作符交于算法接口,全部由应用开发者实现,提供最大的灵活度
  • 编码
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;


public class CustomComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
    /**
     *
     * @param dataSourceNames 数据源集合
     *                      在分库时值为所有分片库的集合 databaseNames
     *                      分表时为对应分片库中所有分片表的集合 tablesNames
     *
     * @param complexKeysShardingValue  分片属性,包括
     *                                  logicTableName 为逻辑表,
     *                                  columnName 分片健(字段),
     *                                  value 为从 SQL 中解析出的分片健的值
     * @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<>();
        // 对两个分片健取模的方式 product_order_0_0、product_order_0_1、product_order_1_0、product_order_1_1
        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;
    }

}

配置文件

## 复合分片算法,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

五、Sharding-Jdbc分片策略实战之Hint分片算法

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

分表

import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;

import java.util.ArrayList;
import java.util.Collection;


public class CustomTableHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
    /**
     *
     * @param dataSourceNames 数据源集合
     *                      在分库时值为所有分片库的集合 databaseNames
     *                      分表时为对应分片库中所有分片表的集合 tablesNames
     *
     * @param hitShardingValue  分片属性,包括
     *                                  logicTableName 为逻辑表,
     *                                  columnName 分片健(字段),hit策略此处为空 ""
     *
     *                                  value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断
     *                                   HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过
     *                                   hintManager.addTableShardingValue("product_order", 1)参数进行指定
     * @return
     */

    @Override
    public Collection<String> doSharding(Collection<String> dataSourceNames, HintShardingValue<Long> hitShardingValue) {

        Collection<String> result = new ArrayList<>();
        for(String datasourceName: dataSourceNames){

            for(Long shardingValue : hitShardingValue.getValues()){

                String value = shardingValue % dataSourceNames.size()+"";

                if(datasourceName.endsWith(value)){
                    result.add(datasourceName);
                }
            }
        }
        return result;
    }
}

分库

import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;

import java.util.ArrayList;
import java.util.Collection;


public class CustomDBHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
    /**
     *
     * @param dataSourceNames 数据源集合
     *                      在分库时值为所有分片库的集合 databaseNames
     *                      分表时为对应分片库中所有分片表的集合 tablesNames
     *
     * @param hitShardingValue  分片属性,包括
     *                                  logicTableName 为逻辑表,
     *                                  columnName 分片健(字段),hit策略此处为空 ""
     *
     *                                  value 【之前】都是 从 SQL 中解析出的分片健的值,用于取模判断
     *                                   HintShardingAlgorithm不再从SQL 解析中获取值,而是直接通过
     *                                   hintManager.addTableShardingValue("product_order", 1)参数进行指定
     * @return
     */

    @Override
    public Collection<String> doSharding(Collection<String> dataSourceNames, HintShardingValue<Long> hitShardingValue) {

        Collection<String> result = new ArrayList<>();
        for(String datasourceName: dataSourceNames){

            for(Long shardingValue : hitShardingValue.getValues()){

                String value = shardingValue % dataSourceNames.size()+"";

                if(datasourceName.endsWith(value)){
                    result.add(datasourceName);
                }
            }
        }
        return result;
    }
}

配置文件

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

测试

    /**
     * 正常可以用AOP进行实现
     */
    @Test
    public void testHit(){

        //清除历史规则
        HintManager.clear();

        //获取对应的实例
        HintManager hintManager = HintManager.getInstance();

        //设置库的分片键值,value是用于库分片取模
        hintManager.addDatabaseShardingValue("product_order",3L);

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

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

        //对应的value,只做查询,不做sql解析
        productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().eq("id",66L));

    }
温馨提示

OK,大功告成![aru_41]
让我来用大白话说说这个hint有什么用。我们之前的分片规则是判断的sql后面的数字,解析的sql里面的数字然后取模的规则,现在我们将这个操作提到了代码层,相信看测试代码的地方已经很明显了。好好看看就完全明白啦![aru_97]

发表评论 取消回复
表情 图片 链接 代码

分享