sharding-jdbc配置

发布时间 2023-08-05 16:54:57作者: yifanSJ

一、概念先行

1)SQL相关的

  • 逻辑表:水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为2张表,分别是t_order_0到t_order_1,他们的逻辑表名为t_order。
  • 真实表:在分片的数据库中真实存在的物理表。例:示例中的t_order_0到t_order_1
  • 数据节点:数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0;ds_0.t_order_1;
  • 绑定表:指分片规则一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
  • 广播表:指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表,示例中的t

2)分片相关

  • 分片键:用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。 SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片。
  • 分片算法:通过分片算法将数据分片,支持通过=、>=、<=、>、<、BETWEEN和IN分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。
    目前提供4种分片算法:
  1. 精确分片算法:对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。
  2. 范围分片算法:对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND、>、<、>=、<=进行分片的场景。需要配合StandardShardingStrategy使用。
  3. 复合分片算法:对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
  4. Hint分片算法:对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。
  • 分片策略:包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。

    目前提供5种分片策略。

  1. 标准分片策略:对应StandardShardingStrategy。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND, >, <, >=, <=分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。
  2. 复合分片策略:对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。
  3. 行表达式分片策略:对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
  4. Hint分片策略:对应HintShardingStrategy。通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。
  5. 不分片策略:对应NoneShardingStrategy。不分片的策略。

3)配置相关

  • 分片规则:分片规则配置的总入口。包含数据源配置、表配置、绑定表配置以及读写分离配置等。
  • 数据源配置:真实数据源列表。
  • 表配置:逻辑表名称、数据节点与分表规则的配置
  • 数据节点配置:用于配置逻辑表与真实表的映射关系。
  • 分片策略配置:
    数据源分片策略:对应于DatabaseShardingStrategy。用于配置数据被分配的目标数据源。
    表分片策略:对应于TableShardingStrategy。用于配置数据被分配的目标表,该目标表存在与该数据的目标数据源内。故表分片策略是依赖与数据源分片策略的结果的。
  • 自增主键生成策略:通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。(雪花算法)

sharding-jdbc配置

sharding:
  jdbc:
    datasource:
      names: ds0 #指定数据源 名称可以自定义,注意:名称要跟后面的配置一致
      ds0: #配置数据源的连接信息
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://192.168.52.10:3306/ball?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT&useSSL=false&allowPublicKeyRetrieval=true
        username: root
        password: Yifan123.
    config:
      sharding:
        props:
          sql.show: true #是否输出sql
        tables:
          sys_role: #逻辑表名
            key-generator-column-name: id #主键
            #分库分表数据节点
#            actual-data-nodes: ds$->{0..2}.t_order$->{0..1}               # 数据节点:多数据源$->{0..N}.逻辑表名$->{0..N}相同表
#            actual-data-nodes: ds0.t_order$->{0..1},ds1.t_order$->{2..4}  # 数据节点:多数据源$->{0..N}.逻辑表名$->{0..N}不同表
#            actual-data-nodes: ds0.t_order$->{0..4}                       # 指定单数据源的配置方式
#            actual-data-nodes: ds0.t_order0,ds1.t_order0,ds0.t_order1,ds1.t_order1 # 全部手动指定
            actual-data-nodes: ds0.sys_role${0..1}    #数据节点,均匀分布
            #数据源分片策略
#            database-strategy:
#              inline: #行表达式
#                sharding-column: id #按照指定列进行分库---分库策略使用ID字段取模
#                algorithm-expression: ds$->{user_id%2}  #按模运算分配
            #分表策略
            table-strategy:
              #标准分片算法
              standard:
                sharding-column: id
                # 精确分片算法,用于 = 和 IN。该类需实现 PreciseShardingAlgorithm 接口并提供无参数的构造器
                precise-algorithm-class-name: com.chain.utils.MyPreciseShardingAlgorithm
                # 范围分片算法类名称,用于 范围查询 可选。该类需实现 RangeShardingAlgorithm 接口并提供无参数的构造器
                range-algorithm-class-name: com.chain.utils.MyRangeShardingAlgorithm # inline: #行表达式 # sharding
-column: id #按照指定列进行分表---分表策略使用ID字段取模 # algorithm-expression: sys_role${id % 2} #按模运算分配

ShardingJDBC提供了5种分片策略及分片算法

一、标准分片策略 StandardShardingStrategyConfiguration

支持单个分片键,提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持

1. 精确分片算法 MyPreciseShardingAlgorithm(必选)

/**
 * 精确匹配查询,需要实现PreciseShardingAlgorithm,可以实现对 `=`以及`in`的查询
 */
public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    /**
     * 精确匹配查询
     *
     * @param tbNames       数据库中所有的事实表
     * @param shardingValue 分片相关信息
     * @return 返回匹配的数据源
     */
    @Override
    public String doSharding(Collection<String> tbNames, PreciseShardingValue<Long> shardingValue) {
        for (String tableName : tbNames) {
            /*
             * shardingValue.getValue() 为分片建的值,比如 id=2时,value就是 2
             * 比如:表分为user_1到user_6,id=1操作user_1表,id=6操作user_6表
             *
             * `+ 6`的目的是为了保证,id=6操作user_6表,运维6%6=0,需要再进行`+6`
             */
            long index = shardingValue.getValue() % tbNames.size();
            //String value = String.valueOf(index == 0 ? index + 6 : index);
            // 匹配满足当前分片规则的表名称
            if (tableName.endsWith(String.valueOf(index))) {
                return tableName;
            }
        }
        throw new RuntimeException("数据库不存在");
    }
}
View Code

2. 范围分片算法 MyRangeShardingAlgorithm(非必选,不配置则全库路由处理)

public class MyRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
    @Override
    public Collection<String> doSharding(Collection<String> tbNames, RangeShardingValue<Long> rangeShardingValue) {
        // 获取逻辑表名称
        String logicTableName = rangeShardingValue.getLogicTableName();

        // between and 的起始值,需要处理只有最大值或者只有最小值的情况
        boolean hasLowerBound = rangeShardingValue.getValueRange().hasLowerBound();
        boolean hasUpperBound = rangeShardingValue.getValueRange().hasUpperBound();

        // 只有最小值,比如:id > x
        if (hasLowerBound && !hasUpperBound) {
            // 直接返回所有表名称
            return tbNames;
        }

        // 只有最大值,比如:id < x
        if (!hasLowerBound && hasUpperBound) {
            long upper = rangeShardingValue.getValueRange().upperEndpoint();
            if (upper < tbNames.size()) {
                // 如果最大值小于表的总数,则返回需要的表名
                return matchMinAndMax(1, upper, logicTableName, tbNames);
            } else {
                // 如果最大值大于表的总数,则返回所有
                return tbNames;
            }
        }

        long lower = Long.valueOf(rangeShardingValue.getValueRange().lowerEndpoint());
        long upper = Long.valueOf(rangeShardingValue.getValueRange().upperEndpoint());

        // 拼接事实表名称
        return matchMinAndMax(lower, upper, logicTableName, tbNames);
    }

    private List<String> matchMinAndMax(long lower, long upper, String logicTableName, Collection<String> tbNames) {
        List<String> tableNameList = new ArrayList<>();
        for (long index = lower; index <= upper; index++) {
            long tableNum = index % tbNames.size();
            // 事实表后缀
            //String suffix = String.valueOf(tableNum == 0 ? tableNum + 6 : tableNum);
            String tableName = logicTableName + tableNum;
            if (tbNames.contains(tableName)) {
                // 添加满足要求的表名称
                tableNameList.add(tableName);
            }

            // 如果满足要求的表已经覆盖了所有表,此处处理是为了方式查询区间过大,而分表不多,导致的过度遍历
            if (tableNameList.size() == tbNames.size()) {
                return tableNameList;
            }
        }
        return tableNameList;
    }
}
View Code

二、复合分片策略 ComplexShardingStrategyConfiguration

支持多个分片键,提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。

 

三、Inline表达式分片策略 InlineShardingStrategyConfiguration

使用Groovy的Inline表达式,提供对SQL语句中的=和IN的分片操作支持

四、Hint分片策略 HintShardingStrategyConfiguration

通过Hint而非SQL解析的方式分片的策略

五、不分片的策略 NoneShardingStrategyConfiguration