18. sharding-jdbc源碼之復(fù)雜路由實現(xiàn)

阿飛Javaer,轉(zhuǎn)載請注明原創(chuàng)出處,謝謝!

路由條件

ParsingSQLRouter.java中決定是簡單路由還是復(fù)雜路由的條件如下;

private RoutingResult route(final List<Object> parameters, final SQLStatement sqlStatement) {
    Collection<String> tableNames = sqlStatement.getTables().getTableNames();
    RoutingEngine routingEngine;
    if (1 == tableNames.size()
            || shardingRule.isAllBindingTables(tableNames)
            || shardingRule.isAllInDefaultDataSource(tableNames)) {
        routingEngine = new SimpleRoutingEngine(shardingRule, parameters, tableNames.iterator().next(), sqlStatement);
    } else {
        // TODO config for cartesian set
        routingEngine = new ComplexRoutingEngine(shardingRule, parameters, tableNames, sqlStatement);
    }
    return routingEngine.route();
}
  • 是否只有一張表--tableNames.size()

說明:這個"一張表"并不是指SQL中只有一張表,而是有分庫分表規(guī)則的表數(shù)量,例如下面這段構(gòu)造ShardingRule的源碼,tableRules()有兩個表,所以tableNames.size()的值為2;如果(Arrays.asList(orderTableRule))即只有1個表,那么tableNames.size()的值為1;

ShardingRule.builder()
.dataSourceRule(dataSourceRule)
.tableRules(Arrays.asList(orderTableRule, userTableRule))
.databaseShardingStrategy(*** ***).tableShardingStrategy(*** ***) .build();
  • 是否都是綁定表--shardingRule.isAllBindingTables(tableNames)

說明:isAllBindingTables(tableNames)判斷tableNames是否都屬于綁定表,例如下面這段構(gòu)造ShardingRule的源碼,.bindingTableRules()里的參數(shù)就是綁定表集合,這里是t_order和t_order_item都是綁定表,那么:SELECT od.user_id, od.order_id, oi.item_id, od.status FROM t_order od join t_order_item oi on od.order_id=oi.order_id這個SQL只有t_order和t_order_item兩個表且都是綁定表,那么shardingRule.isAllBindingTables(tableNames)為true;

ShardingRule.builder()
.dataSourceRule(dataSourceRule)
.tableRules(Arrays.asList(orderTableRule, orderItemTableRule, userTableRule))
.bindingTableRules(Collections.singletonList(new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule))))
. *** ***;       
  • 是否都在默認(rèn)數(shù)據(jù)源中--shardingRule.isAllInDefaultDataSource(tableNames)

說明:sharding-jdbc判斷邏輯源碼如下,即只要在表規(guī)則集合中能夠匹配到邏輯表,就認(rèn)為不屬于默認(rèn)數(shù)據(jù)源中(默認(rèn)數(shù)據(jù)源不分庫分表),例如ShardingRule.builder().dataSourceRule(dataSourceRule).tableRules(Arrays.asList(orderTableRule, orderItemTableRule, userTableRule)),根據(jù)tableRules參數(shù)可知,主要SQL中有t_user,t_ordert_order_item三個表的任意一個表,那么shardingRule.isAllInDefaultDataSource(tableNames)都為false;

public boolean isAllInDefaultDataSource(final Collection<String> logicTables) {
    for (String each : logicTables) {
        if (tryFindTableRule(each).isPresent()) {
            return false;
        }
    }
    return !logicTables.isEmpty();
}

public Optional<TableRule> tryFindTableRule(final String logicTableName) {
    for (TableRule each : tableRules) {
        if (each.getLogicTable().equalsIgnoreCase(logicTableName)) {
            return Optional.of(each);
        }
    }
    return Optional.absent();
}   

構(gòu)造復(fù)雜路由

綜上分析,如果三個條件都不滿足就走復(fù)雜路由ComplexRoutingEngine,構(gòu)造這種場景:
t_order和t_order_item分庫分表且綁定表關(guān)系,加入一個新的分庫分表t_user;ShardingRule如下:

ShardingRule shardingRule = ShardingRule.builder()
        .dataSourceRule(dataSourceRule)
        .tableRules(Arrays.asList(orderTableRule, orderItemTableRule, userTableRule))
        .bindingTableRules(Collections.singletonList(new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule))))
        .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm()))
        .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm()))
        .build();

執(zhí)行的SQL為:

SELECT od.user_id, od.order_id, oi.item_id, od.status 
FROM `t_user` tu 
join t_order od on tu.user_id=od.user_id 
join t_order_item oi on od.order_id=oi.order_id 
where tu.`status`='VALID' and tu.user_id=?

構(gòu)造的這個場景:tableNames.size()=3(三張表t_user,t_order,t_order_item都有分庫分表規(guī)則,所以值為3),shardingRule.isAllBindingTables(tableNames)為false(t_user表不屬于綁定表范圍);shardingRule.isAllInDefaultDataSource(tableNames)為false(三張表都不屬于默認(rèn)數(shù)據(jù)源中的表);所以這個SQL會走復(fù)雜路由的邏輯;

ComplexRoutingEngine

復(fù)雜路由引擎的核心邏輯就是拆分成多個簡單路由,然后求笛卡爾積,復(fù)雜路由核心源碼如下:

@RequiredArgsConstructor
@Slf4j
public final class ComplexRoutingEngine implements RoutingEngine {
    
    // 分庫分表規(guī)則
    private final ShardingRule shardingRule;
    
    // SQL請求參數(shù),豬油一個user_id的值為10
    private final List<Object> parameters;
    
    // 邏輯表集合:t_order,t_order_item,t_user,三個邏輯表
    private final Collection<String> logicTables;
    
    // SQL解析結(jié)果
    private final SQLStatement sqlStatement;
    
    // 復(fù)雜路由的核心邏輯
    @Override
    public RoutingResult route() {
        Collection<RoutingResult> result = new ArrayList<>(logicTables.size());
        Collection<String> bindingTableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        // 遍歷邏輯表集合
        for (String each : logicTables) {
            Optional<TableRule> tableRule = shardingRule.tryFindTableRule(each);
            // 如果遍歷的表配置了分庫分表規(guī)則
            if (tableRule.isPresent()) {
                // 如果綁定關(guān)系表已經(jīng)處理過,那么不需要再處理,例如t_order處理過,由于t_order_item與其是綁定關(guān)系,那么不需要再處理
                if (!bindingTableNames.contains(each)) {
                    // 根據(jù)當(dāng)前遍歷的邏輯表構(gòu)造一個簡單路由規(guī)則
                    result.add(new SimpleRoutingEngine(shardingRule, parameters, tableRule.get().getLogicTable(), sqlStatement).route());
                }

                // 根據(jù)當(dāng)前邏輯表,查找其對應(yīng)的所有綁定表,例如根據(jù)t_order就能夠查詢出t_order和t_order_item;假如配置了.bindingTableRules(***t_point, t_point_detail***),那么,根據(jù)t_point能查詢出t_point和t_point_detail,其目的是N個綁定表只需要路由一個綁定表即可,因為綁定表之間的路由關(guān)系完全一致。
                Optional<BindingTableRule> bindingTableRule = shardingRule.findBindingTableRule(each);
                if (bindingTableRule.isPresent()) {
                    bindingTableNames.addAll(Lists.transform(bindingTableRule.get().getTableRules(), new Function<TableRule, String>() {
                        
                        @Override
                        public String apply(final TableRule input) {
                            return input.getLogicTable();
                        }
                    }));
                }
            }
        }
        log.trace("mixed tables sharding result: {}", result);
        // 如果是復(fù)雜路由,但是路由結(jié)果為空,那么拋出異常
        if (result.isEmpty()) {
            throw new ShardingJdbcException("Cannot find table rule and default data source with logic tables: '%s'", logicTables);
        }
        // 如果結(jié)果的size為1,那么直接返回即可
        if (1 == result.size()) {
            return result.iterator().next();
        }
        // 對剛剛的路由結(jié)果集合計算笛卡爾積,就是最終復(fù)雜的路由結(jié)果
        return new CartesianRoutingEngine(result).route();
    }
}

由上面源碼分析可知,會分別對t_user和t_order構(gòu)造簡單路由(t_order_item和t_order是綁定關(guān)系,二者取其一即可);

  • t_user只分庫不分表(因為構(gòu)造TableRule時邏輯表和實際表一致),且請求參數(shù)為user_id=10,所以t_user這個邏輯表的簡單路由結(jié)果為:數(shù)據(jù)源ds_jdbc_0,實際表t_user;
  • t_order分庫分表,且請求參數(shù)user_id被解析為t_user的條件(笛卡爾積路由引擎會處理),所以t_order的簡單路由結(jié)果為:數(shù)據(jù)源ds_jdbc_0和ds_jdbc_1,實際表t_order_0和t_order_1;

debug的result如下:


result detail

CartesianRoutingEngine

如上分析,求得簡單路由結(jié)果集后,求笛卡爾積就是復(fù)雜路由的最終路由結(jié)果,笛卡爾積路由引擎CartesianRoutingEngine的核心源碼如下:

@RequiredArgsConstructor
@Slf4j
public final class CartesianRoutingEngine implements RoutingEngine {
    
    private final Collection<RoutingResult> routingResults;
    
    @Override
    public CartesianRoutingResult route() {
        CartesianRoutingResult result = new CartesianRoutingResult();
        // getDataSourceLogicTablesMap()的分析參考下面的分析
        for (Entry<String, Set<String>> entry : getDataSourceLogicTablesMap().entrySet()) {
            // 根據(jù)數(shù)據(jù)源&邏輯表,得到實際表集合,即[["t_user"],["t_order_0","t_order_1"]]
            List<Set<String>> actualTableGroups = getActualTableGroups(entry.getKey(), entry.getValue());
            // 把邏輯表名封裝,TableUnit的屬性有:數(shù)據(jù)源名稱,邏輯表名,實際表名(這三個屬性才能確定最終訪問的表)
            List<Set<TableUnit>> tableUnitGroups = toTableUnitGroups(entry.getKey(), actualTableGroups);
            // 計算所有實際表的笛卡爾積
            result.merge(entry.getKey(), getCartesianTableReferences(Sets.cartesianProduct(tableUnitGroups)));
        }
        log.trace("cartesian tables sharding result: {}", result);
        return result;
    }
    
    // 得到數(shù)據(jù)源-邏輯表集合組成的Map
    private Map<String, Set<String>> getDataSourceLogicTablesMap() {
        // 這里很關(guān)鍵,是得到數(shù)據(jù)源的交集(上面分析時t_user邏輯表路由到數(shù)據(jù)源ds_jdbc_0,而t_order表路由到數(shù)據(jù)源ds_jdbc_0和ds_jdbc_1,數(shù)據(jù)源交集就是ds_jdbc_0)
        Collection<String> intersectionDataSources = getIntersectionDataSources();
        Map<String, Set<String>> result = new HashMap<>(routingResults.size());
        for (RoutingResult each : routingResults) {
            for (Entry<String, Set<String>> entry : each.getTableUnits().getDataSourceLogicTablesMap(intersectionDataSources).entrySet()) {
                if (result.containsKey(entry.getKey())) {
                    result.get(entry.getKey()).addAll(entry.getValue());
                } else {
                    result.put(entry.getKey(), entry.getValue());
                }
            }
        }
        // 得到的最終結(jié)果為數(shù)據(jù)源-邏輯表集合組成的Map,這里就是{"ds_jdbc_0":["t_order", "t_user"]}
        return result;
    }
    ... ...
}

計算得到的笛卡爾積結(jié)果如下:


image.png

sql.show結(jié)果如下,可以看到重寫后的2條實際SQL:t_user&t_order_0,以及t_user&t_order_1(t_order_item與t_order是綁定表,保持一致即可):

[INFO ] 2018-05-08 11:13:02,044 --main-- [Sharding-JDBC-SQL] Logic SQL: SELECT od.user_id, od.order_id, oi.item_id, od.status FROM `t_user` tu join t_order od on tu.user_id=od.user_id join t_order_item oi on od.order_id=oi.order_id where tu.`status`='VALID' and tu.user_id=? 
... ...
[INFO ] 2018-05-08 11:13:02,059 --main-- [Sharding-JDBC-SQL] Actual SQL: ds_jdbc_0 ::: SELECT od.user_id, od.order_id, oi.item_id, od.status FROM t_user tu join t_order_0 od on tu.user_id=od.user_id join t_order_item_0 oi on od.order_id=oi.order_id where tu.`status`='VALID' and tu.user_id=? ::: [10] 
[INFO ] 2018-05-08 11:13:02,059 --main-- [Sharding-JDBC-SQL] Actual SQL: ds_jdbc_0 ::: SELECT od.user_id, od.order_id, oi.item_id, od.status FROM t_user tu join t_order_1 od on tu.user_id=od.user_id join t_order_item_1 oi on od.order_id=oi.order_id where tu.`status`='VALID' and tu.user_id=? ::: [10] 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容