sharding-jdbc系列之分片算法(四)

 

前言

通过前文,我们可以知道,不管是简单路由还是复杂路由,最终都是通过SQL计算出最小执行单元,

也就计算该SQL涉及到哪些数据源,哪些表。 在计算这个的时候,都会用到通过ShardingStrategy

这个类来处理。今天主要就是讲这个类。

ShardingStrategy

这个类都会通过当前的分表规则获取。

代码片段如下:

分库

private Collection<String> routeDataSources(final TableRule tableRule) {
          // 根据tableRule获取分库规则
        DatabaseShardingStrategy strategy = shardingRule.getDatabaseShardingStrategy(tableRule);
        // ... 省略代码
          // 静态分片
        Collection<String> result = strategy.doStaticSharding(sqlStatement.getType(), tableRule.getActualDatasourceNames(), shardingValues);
        return result;
    }

分库规则没有动态分片,仅有静态分片。

分表

private Collection<String> routeTables(final TableRule tableRule, final Collection<String> routedDataSources) {
          // 获取分表规则
        TableShardingStrategy strategy = shardingRule.getTableShardingStrategy(tableRule);
          // 判断是静态分片,还是动态分片。
        Collection<String> result = tableRule.isDynamic() ? strategy.doDynamicSharding(shardingValues)
                : strategy.doStaticSharding(sqlStatement.getType(), tableRule.getActualTableNames(routedDataSources), shardingValues);
        // 省略代码N行
        return result;
    }

静态分片

/**
     * 计算静态分片.
     *
     * @param sqlType SQL语句的类型
     * @param availableTargetNames 所有的可用分片资源集合
     * @param shardingValues 分片值集合
     * @return 分库后指向的数据源名称集合
     */
    public Collection<String> doStaticSharding(final SQLType sqlType, final Collection<String> availableTargetNames, final Collection<ShardingValue<?>> shardingValues) {
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
          // 判断分片值是否为空
        if (shardingValues.isEmpty()) {
              // 否则,直接返回所有目标资源
            result.addAll(availableTargetNames);
        } else {
              // 调用分片方法
            result.addAll(doSharding(shardingValues, availableTargetNames));
        }
        return result;
    }

PS : 本文源码是根据1.5.4的版本说的,之前的版本在分片值为空的时候,还需要做以下判断 , 在本版本里面,作者把这个校验代码摘除了

,这也导致了,如果做了分表,但是insert语句里面不包含分表的分片字段,**那么将会把路由的到的表里面全部插入该数据。 **

// 分片值为空,则判断SQLType是否为insert类型 ,如果是insert类型,则报异常
Preconditions.checkState(!isInsertMultiple(sqlType, availableTargetNames), "");

动态分片

/**
     * 计算动态分片.
     *
     * @param shardingValues 分片值集合
     * @return 分库后指向的分片资源集合
     */
    public Collection<String> doDynamicSharding(final Collection<ShardingValue<?>> shardingValues) {
          // 动态分片,分片值必须要有,否则报错。
        Preconditions.checkState(!shardingValues.isEmpty(), "Dynamic table should contain sharding value.");
        Collection<String> availableTargetNames = Collections.emptyList();
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
          // 调用分片算法
        result.addAll(doSharding(shardingValues, availableTargetNames));
        return result;
    }

分片算法

@SuppressWarnings({ "unchecked", "rawtypes" })
    private Collection<String> doSharding(final Collection<ShardingValue<?>> shardingValues, final Collection<String> availableTargetNames) {
          // 分片规则是NoneKeyShardingAlgorithm ,表示没有分片,直接返回。
        if (shardingAlgorithm instanceof NoneKeyShardingAlgorithm) {
            return Collections.singletonList(((
              NoneKeyShardingAlgorithm) shardingAlgorithm).doSharding(
              availableTargetNames, shardingValues.iterator().next()));
        }
          // 单键分片 
        if (shardingAlgorithm instanceof SingleKeyShardingAlgorithm) {
            SingleKeyShardingAlgorithm<?> singleKeyShardingAlgorithm 
              = (SingleKeyShardingAlgorithm<?>) shardingAlgorithm;
            ShardingValue shardingValue = shardingValues.iterator().next();
            switch (shardingValue.getType()) {
                case SINGLE: // ==条件
                    return Collections.singletonList(singleKeyShardingAlgorithm.doEqualSharding(
                      availableTargetNames, shardingValue));
                case LIST: // in 查找
                    return singleKeyShardingAlgorithm.doInSharding(
                      availableTargetNames, shardingValue);
                case RANGE: // Between 范围查找
                    return singleKeyShardingAlgorithm.doBetweenSharding(
                      availableTargetNames, shardingValue);
                default:
                    throw new UnsupportedOperationException(
                      shardingValue.getType().getClass().getName());
            }
        }
          // 多键分片
        if (shardingAlgorithm instanceof MultipleKeysShardingAlgorithm) {
            return ((MultipleKeysShardingAlgorithm) shardingAlgorithm).doSharding(
              availableTargetNames, shardingValues);
        }
        throw new UnsupportedOperationException(shardingAlgorithm.getClass().getName());
    }

在单键分片那个switch里面,通过分片的操作类型,分别调用我们自定义的分片策略的

doEqualSharding

doInSharding

doBetweenSharding

这三个方法,然后获取返回计算出的分片结果,

单键分片静态分片例子

/**
 * 分表策略的基本实现
 */
public class ModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {

    @Override
    public String doEqualSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
        for (String each : tableNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new IllegalArgumentException();
    }

    @Override
    public Collection<String> doInSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<String>(tableNames.size());
        for (Long value : shardingValue.getValues()) {
            for (String tableName : tableNames) {
                if (tableName.endsWith(value % 2 + "")) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }

    @Override
    public Collection<String> doBetweenSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<String>(tableNames.size());
        Range<Long> range = (Range<Long>) shardingValue.getValueRange();
        for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
            for (String each : tableNames) {
                if (each.endsWith(i % 2 + "")) {
                    result.add(each);
                }
            }
        }
        return result;
    }
}

单键分片动态分片例子

public final class SingleKeyDynamicModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Integer> {

    private final String tablePrefix;

    @Override
    public String doEqualSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
        return tablePrefix + shardingValue.getValue() % 10;
    }

    @Override
    public Collection<String> doInSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(shardingValue.getValues().size());
        for (Integer value : shardingValue.getValues()) {
            result.add(tablePrefix + value % 10);
        }
        return result;
    }

    @Override
    public Collection<String> doBetweenSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
        Range<Integer> range = shardingValue.getValueRange();
        for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
            result.add(tablePrefix + i % 10);
        }
        return result;
    }
}

静态分片和动态分片的区别在于,静态分片是已经定义好了真实表的,动态分片在于表名可以在分片算法动态计算出来,动态分片,可以做按月动态分表。

猜你喜欢

转载自blog.csdn.net/u012394095/article/details/81449541
今日推荐