Sharding-jdbc之分片

sharding-jdbc的作用就不必做過多的解釋了,主要用途是分庫分表;首先先了解下分庫分表的策略吧。

1 分片鍵

分片鍵即在分庫分表時根據表中的某一個或者多個字段爲依據,以一定的邏輯進行分割,例如根據自增的主鍵求模的規則進行分片等。

2 分片算法

分片算法結合分片鍵對具體的數據進行分片;支持通過=、BETWEEN和IN分片。分片算法需要應用方開發者自行實現,可實現的靈活度非常高。分片算法有四種:
(1)精確分片算法
對應PreciseShardingAlgorithm,用於處理使用單一鍵作爲分片鍵的=與IN進行分片的場景。需要配合standardShardingStrategy使用。

(2)範圍分片算法
對應RangeShardingAlgorithm,用於處理使用單一鍵作爲分片鍵的BETWEEN AND進行分片的場景。需要配合StandardShardingStrategy使用。

(3)複合分片算法
對應ComplexKeysShardingAlgorithm,用於處理使用多鍵作爲分片鍵進行分片的場景,包含多個分片鍵的邏輯較複雜,需要應用開發者自行處理其中的複雜度。需要配合ComplexShardingStrategy使用。

(4) Hint分片算法
對應HintShardingAlgorithm,用於處理使用Hint行分片的場景。需要配合HintShardingStrategy使用。

3 分片策略

包含分片鍵和分片算法,由於分片算法的獨立性,將其獨立抽離。真正可用於分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供5種分片策略。

(1)標準分片策略

對應StandardShardingStrategy。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法。PreciseShardingAlgorithm是必選的,用於處理=和IN的分片。RangeShardingAlgorithm是可選的,用於處理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫路由處理。

public final class StandardShardingStrategy implements ShardingStrategy {
    //分片鍵 支支持單分片鍵
    private final String shardingColumn;
    //精確分片算法
    private final PreciseShardingAlgorithm preciseShardingAlgorithm;
    //範圍分片算法
    private final RangeShardingAlgorithm rangeShardingAlgorithm;
    //
    public StandardShardingStrategy(final StandardShardingStrategyConfiguration standardShardingStrategyConfig) {
    //校驗分片鍵 不能爲空
        Preconditions.checkNotNull(standardShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
        //校驗精確分片算法,不能爲空
        Preconditions.checkNotNull(standardShardingStrategyConfig.getPreciseShardingAlgorithm(), "precise sharding algorithm cannot be null.");
        //初始化分片策略的屬性
        shardingColumn = standardShardingStrategyConfig.getShardingColumn();
        preciseShardingAlgorithm = standardShardingStrategyConfig.getPreciseShardingAlgorithm();
        //範圍分片算法可以爲空
        rangeShardingAlgorithm = standardShardingStrategyConfig.getRangeShardingAlgorithm();
    }
    
    //執行分片
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue> shardingValues) {
    //ShardingValue 提供獲取邏輯表和分片鍵方法
        ShardingValue shardingValue = shardingValues.iterator().next();
        //判斷ShardingValue 分片操作是精確分片還是範圍分片,從而執行相應的算法 ;ListShardingValue包含邏輯表、分片鍵、精確匹配值集合
        Collection<String> shardingResult = shardingValue instanceof ListShardingValue
                ? doSharding(availableTargetNames, (ListShardingValue) shardingValue) : doSharding(availableTargetNames, (RangeShardingValue) shardingValue);
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.addAll(shardingResult);
        return result;
    }
    //範圍分片算法
    @SuppressWarnings("unchecked")
    private Collection<String> doSharding(final Collection<String> availableTargetNames, final RangeShardingValue<?> shardingValue) {
    //如果範圍分片算法爲空
        if (null == rangeShardingAlgorithm) {
            throw new UnsupportedOperationException("Cannot find range sharding strategy in sharding rule.");
        }
        return rangeShardingAlgorithm.doSharding(availableTargetNames, shardingValue);
    }
    //精確分片算法
    @SuppressWarnings("unchecked")
    private Collection<String> doSharding(final Collection<String> availableTargetNames, final ListShardingValue<?> shardingValue) {
        Collection<String> result = new LinkedList<>();
        for (PreciseShardingValue<?> each : transferToPreciseShardingValues(shardingValue)) {
            String target = preciseShardingAlgorithm.doSharding(availableTargetNames, each);
            if (null != target) {
                result.add(target);
            }
        }
        return result;
    }
    //將精確匹配值集合進行轉換爲精確匹配值
    @SuppressWarnings("unchecked")
    private List<PreciseShardingValue> transferToPreciseShardingValues(final ListShardingValue<?> shardingValue) {
        List<PreciseShardingValue> result = new ArrayList<>(shardingValue.getValues().size());
        for (Comparable<?> each : shardingValue.getValues()) {
            result.add(new PreciseShardingValue(shardingValue.getLogicTableName(), shardingValue.getColumnName(), each));
        }
        return result;
    }
    //獲取分片鍵
    @Override
    public Collection<String> getShardingColumns() {
    //String.CASE_INSENSITIVE_ORDER String排序與字母大小寫無關
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.add(shardingColumn);
        return result;
    }
}

(2)複合分片策略

對應ComplexShardingStrategy。複合分片策略。提供對SQL語句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片鍵,由於多分片鍵之間的關係複雜,因此並未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符透傳至分片算法,完全由應用開發者實現,提供最大的靈活度。

public final class ComplexShardingStrategy implements ShardingStrategy {
    //支持多分片鍵 集合存儲分片鍵
    @Getter
    private final Collection<String> shardingColumns;
    //複合分片算法
    private final ComplexKeysShardingAlgorithm shardingAlgorithm;
    
    public ComplexShardingStrategy(final ComplexShardingStrategyConfiguration complexShardingStrategyConfig) {
        Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingColumns(), "Sharding columns cannot be null.");
        Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
        shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        shardingColumns.addAll(StringUtil.splitWithComma(complexShardingStrategyConfig.getShardingColumns()));
        shardingAlgorithm = complexShardingStrategyConfig.getShardingAlgorithm();
    }
    
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue> shardingValues) {
        Collection<String> shardingResult = shardingAlgorithm.doSharding(availableTargetNames, shardingValues);
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.addAll(shardingResult);
        return result;
    }
}

(3)行表達式分片策略

對應InlineShardingStrategy。使用Groovy的表達式,提供對SQL語句中的=和IN的分片操作支持,只支持單分片鍵。對於簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的Java代碼開發,如: t_user_$->{u_id % 8} 表示t_user表根據u_id模8,而分成8張表,表名稱爲t_user_0到t_user_7。

public final class InlineShardingStrategy implements ShardingStrategy {
   
    private final String shardingColumn;
     //表示Groovy中的任何閉包對象
    private final Closure<?> closure;
    
    public InlineShardingStrategy(final InlineShardingStrategyConfiguration inlineShardingStrategyConfig) {
        Preconditions.checkNotNull(inlineShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
        Preconditions.checkNotNull(inlineShardingStrategyConfig.getAlgorithmExpression(), "Sharding algorithm expression cannot be null.");
        shardingColumn = inlineShardingStrategyConfig.getShardingColumn();
        //分片Groovy表達式
        String algorithmExpression = InlineExpressionParser.handlePlaceHolder(inlineShardingStrategyConfig.getAlgorithmExpression().trim());
        closure = new InlineExpressionParser(algorithmExpression).evaluateClosure();
    }
    
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue> shardingValues) {
        ShardingValue shardingValue = shardingValues.iterator().next();
        //判斷是否是範圍分片
        Preconditions.checkState(shardingValue instanceof ListShardingValue, "Inline strategy cannot support range sharding.");
        Collection<String> shardingResult = doSharding((ListShardingValue) shardingValue);
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.addAll(shardingResult);
        return result;
    }
    //與標準分片策略中的實現相同,不同的是需要通過Groovy進行解析出最終的結果
    private Collection<String> doSharding(final ListShardingValue shardingValue) {
        Collection<String> result = new LinkedList<>();
        for (PreciseShardingValue<?> each : transferToPreciseShardingValues(shardingValue)) {
            result.add(execute(each));
        }
        return result;
    }
    
    @SuppressWarnings("unchecked")
    private List<PreciseShardingValue> transferToPreciseShardingValues(final ListShardingValue<?> shardingValue) {
        List<PreciseShardingValue> result = new ArrayList<>(shardingValue.getValues().size());
        for (Comparable<?> each : shardingValue.getValues()) {
            result.add(new PreciseShardingValue(shardingValue.getLogicTableName(), shardingValue.getColumnName(), each));
        }
        return result;
    }
    //通過Closure解析
    private String execute(final PreciseShardingValue shardingValue) {
        Closure<?> result = closure.rehydrate(new Expando(), null, null);
        result.setResolveStrategy(Closure.DELEGATE_ONLY);
        result.setProperty(shardingValue.getColumnName(), shardingValue.getValue());
        return result.call().toString();
    }
    
    @Override
    public Collection<String> getShardingColumns() {
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.add(shardingColumn);
        return result;
    }
}

(4)Hint分片策略

對應HintShardingStrategy。通過Hint而非SQL解析的方式分片的策略。

public final class HintShardingStrategy implements ShardingStrategy {
    //支持多分片鍵
    @Getter
    private final Collection<String> shardingColumns;
    
    private final HintShardingAlgorithm shardingAlgorithm;
    
    public HintShardingStrategy(final HintShardingStrategyConfiguration hintShardingStrategyConfig) {
        Preconditions.checkNotNull(hintShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
        shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        shardingAlgorithm = hintShardingStrategyConfig.getShardingAlgorithm();
    }
    
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue> shardingValues) {
        Collection<String> shardingResult = shardingAlgorithm.doSharding(availableTargetNames, shardingValues.iterator().next());
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.addAll(shardingResult);
        return result;
    }
}

(5)不分片策略

對應NoneShardingStrategy。不分片的策略。

4 SQL Hint

對於分片字段非SQL決定,而由其他外置條件決定的場景,可使用SQL Hint靈活的注入分片字段;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章