在上一篇,我們介紹了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,再組裝RoutingUnit和TableUnit
}
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做一個總結:
更多內容可以關注我的公衆號:程序員向架構師轉型。