ShardingSphere源碼解析之路由引擎(五)

上一篇,我們介紹了ShardingSphere中綁定表BindingTable的概念,然後基於綁定表分析了StandardRoutingEngine和ComplexRoutingEngine的路由過程。這些都屬於分片路由的範疇,即路由引擎的輸入中帶有分片鍵。今天我們要討論另一大類路由引擎,這些路由路由引擎的輸入中並沒有攜帶分片鍵。

在次之前,我們也再引入一個與路由相關的概念,即廣播表(BroadCastTable)。再次引用ShardingSphere官網的說法,所謂的廣播表是指所有的分片數據源中都存在的表,表結構和表中的數據在每個數據庫中均完全一致。適用於數據量不大且需要與海量數據的表進行關聯查詢的場景,典型的例子就是每個分片中都應該存在的字典表。

在ShardingSphere,根據輸入SQL的類型,存在很多種用於廣播的路由引擎,我們可以回顧RoutingEngineFactory中創建RoutingEngine的方法。

首先,如果輸入的是TCLStatement和DDLStatement,那麼直接執行DatabaseBroadcastRoutingEngine和TableBroadcastRoutingEngine中的路由方法,判斷條件如下所示:

       if (sqlStatement instanceof TCLStatement) {

            return new DatabaseBroadcastRoutingEngine(shardingRule);

        }

        if (sqlStatement instanceof DDLStatement) {

            return new TableBroadcastRoutingEngine(shardingRule, metaData.getTables(), sqlStatementContext);

        }

DatabaseBroadcastRoutingEngine的路由方法非常直接,即基於每個DataSourceName構建一個RoutingUnit,然後再拼裝成RoutingResult,如下所示:

public final class DatabaseBroadcastRoutingEngine implements RoutingEngine {   

    private final ShardingRule shardingRule;   

    @Override

    public RoutingResult route() {

        RoutingResult result = new RoutingResult();

        for (String each : shardingRule.getShardingDataSourceNames().getDataSourceNames()) {

            result.getRoutingUnits().add(new RoutingUnit(each));

        }

        return result;

    }

}

同樣也可以想象TableBroadcastRoutingEngine的實現過程,我們根據logicTableName獲取對應的TableRule,然後根據TableRule中的真實DataNode構建RoutingUnit對象,這一過程如下所示:

private Collection<RoutingUnit> getAllRoutingUnits(final String logicTableName) {

        Collection<RoutingUnit> result = new LinkedList<>();

        TableRule tableRule = shardingRule.getTableRule(logicTableName);

        for (DataNode each : tableRule.getActualDataNodes()) {

            RoutingUnit routingUnit = new RoutingUnit(each.getDataSourceName());

            routingUnit.getTableUnits().add(new TableUnit(logicTableName, each.getTableName()));

            result.add(routingUnit);

        }

        return result;

    }

接着我們來看針對DALStatement的場景,這一場景相對複雜,根據輸入的DALStatement的類型,會有幾個不同的處理分支,如下所示:

private static RoutingEngine getDALRoutingEngine(final ShardingRule shardingRule, final SQLStatement sqlStatement, final Collection<String> tableNames) {

        if (sqlStatement instanceof UseStatement) {

            return new IgnoreRoutingEngine();

        }

        if (sqlStatement instanceof SetStatement || sqlStatement instanceof ResetParameterStatement || sqlStatement instanceof ShowDatabasesStatement) {

            return new DatabaseBroadcastRoutingEngine(shardingRule);

        }

        if (!tableNames.isEmpty() && !shardingRule.tableRuleExists(tableNames) && shardingRule.hasDefaultDataSourceName()) {

            return new DefaultDatabaseRoutingEngine(shardingRule, tableNames);

        }

        if (!tableNames.isEmpty()) {

            return new UnicastRoutingEngine(shardingRule, tableNames);

        }

        return new DataSourceGroupBroadcastRoutingEngine(shardingRule);

}

我們分別來看一下這裏面的幾個路由引擎。首先是最簡單的IgnoreRoutingEngine,即什麼都不做的路由:

public final class IgnoreRoutingEngine implements RoutingEngine {   

    @Override

    public RoutingResult route() {

        return new RoutingResult();

    }

}

本質上,UnicastRoutingEngine代表單播路由,用於獲取某一真實表信息的場景,它僅需要從任意庫中的任意真實表中獲取數據即可。例如DESCRIBE t_order;語句就適合使用UnicastRoutingEngine,因爲t_order的兩個真實表t_order_0,t_order_1的描述結構相同,所以這個命令在任意真實表上選擇執行一次。UnicastRoutingEngine實現過程如下所示,我們裁剪了代碼,而是直接使用註釋來標明每個分支的執行邏輯:

@Override

    public RoutingResult route() {

        RoutingResult result = new RoutingResult();

        if (shardingRule.isAllBroadcastTables(logicTables)) {

            //如果都是廣播表,則對每個logicTable組裝TableUnit,再構建RoutingUnit            

        } else if (logicTables.isEmpty()) {

            //如果表爲null,則直接組裝RoutingUnit,不用構建TableUnit            

        } else if (1 == logicTables.size()) {

            //如果只有一張表,則組裝RoutingUnit和單個表的TableUnit            

        } else {

            //如果存在多個實體表,則先獲取DataSource,再組裝RoutingUnitTableUnit            

        }

        return result;

    }

DefaultDatabaseRoutingEngine,顧名思義是對默認的數據庫執行路由。那麼這個默認數據庫是怎麼來的呢?我們從ShardingRule的ShardingDataSourceNames類中能找到答案。實際上ShardingDataSourceNames依賴於ShardingRuleConfiguration,而在ShardingRuleConfiguration就存在一個配置項defaultDataSourceName,如下所示:

private String defaultDataSourceName;

明白這一點,DefaultDatabaseRoutingEngine的路由過程也就不難理解了,其route方法如下所示:

public RoutingResult route() {

        RoutingResult result = new RoutingResult();

        List<TableUnit> routingTables = new ArrayList<>(logicTables.size());

        for (String each : logicTables) {

            routingTables.add(new TableUnit(each, each));

        }

        RoutingUnit routingUnit = new RoutingUnit(shardingRule.getShardingDataSourceNames().getDefaultDataSourceName());

        routingUnit.getTableUnits().addAll(routingTables);

        result.getRoutingUnits().add(routingUnit);

        return result;

    }

    最後,我們來看一下針對數據控制語言DCLStatement的處理流程。在主從環境下,對於DCLStatement而言,有時候我們希望SQL語句只針對主數據庫進行執行,所以就有了如下所示的MasterInstanceBroadcastRoutingEngine:

@Override

    public RoutingResult route() {

        RoutingResult result = new RoutingResult();

        for (String each : shardingRule.getShardingDataSourceNames().getDataSourceNames()) {

            if (dataSourceMetas.getAllInstanceDataSourceNames().contains(each)) {

                Optional<MasterSlaveRule> masterSlaveRule = shardingRule.findMasterSlaveRule(each);

                if (!masterSlaveRule.isPresent() || masterSlaveRule.get().getMasterDataSourceName().equals(each)) {

                    result.getRoutingUnits().add(new RoutingUnit(each));

                }

            }

        }

        return result;

}

可以看到,這裏引入了一個MasterSlaveRule規則,該規則提供了getMasterDataSourceName()方法以獲取主DataSourceName,這樣我們就可以針對該主數據執行如Grant等數據控制語言。

至此,我們通過前面幾篇文章的介紹,全面瞭解了ShardingSphere中的各種RoutingEngine的實現情況。最後,我們還是引用ShardingSphere官網的一張圖片來對這些RoutingEngine做一個總結:

更多內容可以關注我的公衆號:程序員向架構師轉型。

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