前言
上文ShardingSphere-JDBC分片解析引擎中介紹了分片流程中的解析引擎,重點介紹瞭解析引擎的核心組件ANTLR
;本文繼續介紹分片流程中的路由引擎,路由引擎可以說是整個分片流程的核心模塊,用戶自定義的分片算法都在路由引擎中執行;
開啓日誌
爲了更加清晰詳細的查看路由日誌,開啓SQL_SHOW
功能:
-
引入log4j相關jar,以及log4j.xml配置文件
-
配置
SQL_SHOW
屬性爲開啓:Properties prop = new Properties(); prop.put(ConfigurationPropertyKey.SQL_SHOW.getKey(), true);
路由裝飾器
路由引擎的外層包裝了一層路由裝飾器RouteDecorator
,爲什麼要這麼設計是因爲除了我們正常走路由算法的路由引擎,ShardingSphere-JDBC
還提供了讀寫分離功能,這其實在一定程度上來講也是路由,而且這兩種路由方式是可以疊加的;所有這裏提供了一層抽象,實現類包括:
- MasterSlaveRouteDecorator:讀寫分離路由裝飾器;
- ShardingRouteDecorator:分片路由裝飾器,內部包含了各種路由引擎;
裝飾器可以疊加,所以提供了優先級功能OrderAware
,同時每個裝飾器都有對應的規則,大致如下所示:
裝飾器-RouteDecorator | 配置-Configuration | 規則-BaseRule | 優先級-Order |
---|---|---|---|
MasterSlaveRouteDecorator | MasterSlaveRuleConfiguration | MasterSlaveRule | 10 |
ShardingRouteDecorator | ShardingRuleConfiguration | ShardingRule | 0 |
根據優先級可以知道首先執行ShardingRouteDecorator
,有了路由結果再執行MasterSlaveRouteDecorator
;部分啓動類代碼在DataNodeRouter
中如下所示:
private final Map<BaseRule, RouteDecorator> decorators = new LinkedHashMap<>();
private RouteContext executeRoute(final String sql, final List<Object> parameters, final boolean useCache) {
RouteContext result = createRouteContext(sql, parameters, useCache);
for (Entry<BaseRule, RouteDecorator> entry : decorators.entrySet()) {
result = entry.getValue().decorate(result, metaData, entry.getKey(), properties);
}
return result;
}
decorators
會根據用戶的配置來決定是否會啓動對應的裝飾器,可以參考上面的表格;下面按照優先級分別介紹兩種裝飾器;
分片路由裝飾器
經過解析引擎獲取到了SQLStatement
,想要做分片路由除了此參數還需要另外一個重要參數分片路由規則ShardingRule
;有了這兩個核心參數分片路由大致可以分爲以下幾步:
- 獲取分片條件
ShardingConditions
- 獲取具體分片引擎
ShardingRouteEngine
- 執行路由處理,獲取路由結果
在詳細介紹每一步之前,首先介紹以下幾個核心參數RouteContext
、ShardingRule
;
核心參數
重點看一下RouteContext
、ShardingRule
這兩個核心參數;
RouteContext
路由上下文參數,主要包含如下幾個參數:
public final class RouteContext {
private final SQLStatementContext sqlStatementContext;
private final List<Object> parameters;
private final RouteResult routeResult;
}
- sqlStatementContext:解析引擎獲取的
SQLStatement
; - parameters:
PreparedStatement
中設置的參數,如執行insert操作setXxx
代替?
; - routeResult:路由之後用來存放路由結果;
ShardingRule
分片規則,主要參數如下,這個其實和ShardingRuleConfiguration
大同小異,只是重新做了一個包裝;
public class ShardingRule implements BaseRule {
private final ShardingRuleConfiguration ruleConfiguration;
private final ShardingDataSourceNames shardingDataSourceNames;
private final Collection<TableRule> tableRules;
private final Collection<BindingTableRule> bindingTableRules;
private final Collection<String> broadcastTables;
private final ShardingStrategy defaultDatabaseShardingStrategy;
private final ShardingStrategy defaultTableShardingStrategy;
private final ShardingKeyGenerator defaultShardingKeyGenerator;
private final Collection<MasterSlaveRule> masterSlaveRules;
private final EncryptRule encryptRule;
- ruleConfiguration:路由規則配置,可以理解爲是
ShardingRule
的原始文件; - shardingDataSourceNames:分片的數據源名稱;
- tableRules:表路由規則,對應了用戶配置的
TableRuleConfiguration
; - bindingTableRules:綁定表配置,分⽚規則⼀致的主表和⼦表;
- broadcastTables:廣播表配置,所有的分⽚數據源中都存在的表;
- defaultDatabaseShardingStrategy:默認庫分片策略;
- defaultTableShardingStrategy:默認表分片策略;
- defaultShardingKeyGenerator:默認主鍵生成策略;
- masterSlaveRules:主從規則配置,用來實現讀寫分離的,可配置一個主表多個從表;
- encryptRule:加密規則配置,提供了對某些敏感數據進行加密的功能;
獲取分片條件
在獲取具體路由引擎和執行路由操作之前,我們需要獲取分片的條件,常見的分片條件主要在Insert語句和Where語句後面;部分獲取分片條件的源碼如下:
private ShardingConditions getShardingConditions(final List<Object> parameters,
final SQLStatementContext sqlStatementContext, final SchemaMetaData schemaMetaData, final ShardingRule shardingRule) {
if (sqlStatementContext.getSqlStatement() instanceof DMLStatement) {
if (sqlStatementContext instanceof InsertStatementContext) {
return new ShardingConditions(new InsertClauseShardingConditionEngine(shardingRule).createShardingConditions((InsertStatementContext) sqlStatementContext, parameters));
}
return new ShardingConditions(new WhereClauseShardingConditionEngine(shardingRule, schemaMetaData).createShardingConditions(sqlStatementContext, parameters));
}
return new ShardingConditions(Collections.emptyList());
}
首先會判斷是不是DMLStatement
類型,也是最常見的SQL類型比如:增刪改查;接下來判斷是否是Insert語句,分別使用不同的分片條件生成引擎:
- InsertClauseShardingConditionEngine:處理Insert語句分片條件;
- WhereClauseShardingConditionEngine:處理非Insert語句Where語句的分片條件;
InsertClauseShardingConditionEngine
Insert語句中包含分片條件主要有兩個地方:
-
Insert語句中指定的字段名稱,當然是否是分片條件,還需要檢測
ShardingRule
中的tableRules
是否配置了相關字段作爲分片鍵,看一條簡單的Insert語句:insert into t_order (user_id,order_id) values (?,?)
這一步其實就是檢測user_id和order_id是否在
ShardingRule
中配置成分片鍵; -
當然除了上面顯示指定的字段,還有無需顯示指定的主鍵,如果配置了主鍵生成策略,同樣需要檢測
ShardingRule
中的tableRules
是否配置了相關字段作爲分片鍵;tableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id"));
生成的結果就是ShardingConditions
,內部包含多個ShardingCondition
:
public final class ShardingConditions {
private final List<ShardingCondition> conditions;
}
public class ShardingCondition {
private final List<RouteValue> routeValues = new LinkedList<>();
}
這裏的RouteValue
實現包括ListRouteValue
、RangeRouteValue
、AlwaysFalseRouteValue
:
- ListRouteValue:可以理解分片鍵對應的是一個具體的值,可以是單個也可以多個;
- RangeRouteValue:分片鍵對應的是一個區間值;
- AlwaysFalseRouteValue:總是失敗的路由值;
WhereClauseShardingConditionEngine
常見Select語句,Where語句後面可以包含多個條件,每個條件同樣需要檢測ShardingRule
中的tableRules
是否配置了相關字段作爲分片鍵;稍有不同的地方是,Where條件需要做合併處理,比如:
String sql = "select user_id,order_id from t_order where order_id = 101 and order_id = 101";
String sql = "select user_id,order_id from t_order where order_id = 101 and order_id in(101)";
order_id出現多個值一樣會進行合併處理,這裏會合併成一個order_id = 101
,如果這裏兩個值不一樣比如:
String sql = "select user_id,order_id from t_order where order_id = 101 and order_id = 102";
會返回一個AlwaysFalseRouteValue
,表示這個條件不可能成立;
獲取路由引擎
ShardingSphere-JDBC
根據不同的SQLStatement
提供了10種路由引擎,下面分別介紹,首先看一下大致的流程圖;
流程圖
流程圖大致如上所示,具體查看ShardingRouteEngineFactory
即可;下面詳細介紹每個路由引擎;
路由引擎
ShardingDatabaseBroadcastRoutingEngine
全庫路由引擎:用於處理對數據庫的操作,包括用於庫設置的 SET 類型的數據庫管理命令,以及 TCL 這樣的事務控制語句。 在這種情況下,會根據邏輯庫的名字遍歷所有符合名字匹配的真實庫,並在真實庫中執行該命令;
1.屬於DALStatement
數據庫訪問層,常見的命令包括:set,reset,show databases;
show databases;
以上sql會全庫路由,路由sql如下所示:
Actual SQL: ds0 ::: show databases;
Actual SQL: ds1 ::: show databases;
2.邏輯表都屬於廣播表
insert into t_config (k,v) values (?,?)
t_config
配置的是一張廣播表,執行insert
操作會將數據插入所有庫中;當然前提需要配置廣播表:
Collection<String> broadcastTables = new LinkedList<>();
broadcastTables.add("t_config");
shardingRuleConfig.setBroadcastTables(broadcastTables);
路由日誌如下:
Actual SQL: ds0 ::: insert into t_config (k,v) values (?, ?) ::: [aa1, 1111]
Actual SQL: ds1 ::: insert into t_config (k,v) values (?, ?) ::: [aa1, 1111]
3.屬於TCLStatement
事務控制語言,包括設置保存點,回滾等;
SET autocommit=0
路由日誌如下:
Actual SQL: ds0 ::: SET autocommit=0;
Actual SQL: ds1 ::: SET autocommit=0;
ShardingTableBroadcastRoutingEngine
全庫表路由用於處理對數據庫中與其邏輯表相關的所有真實表的操作,主要包括不帶分片鍵的 DQL 和 DML,以及 DDL 等;
1.屬於DDLStatement
數據庫定義語言,包括創建、修改、刪除表等;
ALTER TABLE t_order MODIFY COLUMN user_id BIGINT(50) NOT NULL;
日誌如下所示:
Actual SQL: ds0 ::: ALTER TABLE t_order0 MODIFY COLUMN user_id BIGINT(50) NOT NULL;
Actual SQL: ds0 ::: ALTER TABLE t_order1 MODIFY COLUMN user_id BIGINT(50) NOT NULL;
Actual SQL: ds1 ::: ALTER TABLE t_order0 MODIFY COLUMN user_id BIGINT(50) NOT NULL;
Actual SQL: ds1 ::: ALTER TABLE t_order1 MODIFY COLUMN user_id BIGINT(50) NOT NULL;
2.屬於DCLStatement
數據庫控制語言,包括授權,角色控制等;grant,deny等命令
grant select on ds.t_order to root@'%'
給用戶root授權select權限,這裏需要指定唯一的表名,不能使用*代替;
Actual SQL: ds0 ::: grant select on t_order0 to root@'%'
Actual SQL: ds0 ::: grant select on t_order1 to root@'%'
Actual SQL: ds1 ::: grant select on t_order0 to root@'%'
Actual SQL: ds1 ::: grant select on t_order1 to root@'%'
ShardingIgnoreRoutingEngine
阻斷路由用於屏蔽 SQL 對數據庫的操作;
1.DALStatement
阻斷路由主要針對DALStatement
下面的use命令;
use ds0
ShardingDefaultDatabaseRoutingEngine
默認數據庫路由,需要配置默認數據源名稱,包含以下幾種情況:
1.屬於DALStatement
show create table t_order1
注意點:這裏表是真實表,不能配置TableRuleConfiguration
,需要配置默認數據源名稱;
shardingRuleConfig.setDefaultDataSourceName("ds0");
日誌如下:
Actual SQL: ds0 ::: show create table t_order1
2.邏輯表都屬於默認數據源
select user_id,order_id from t_order0 where user_id = 102
注意點:這裏表是真實表,不能配置TableRuleConfiguration
,需要配置默認數據源名稱,不能配置爲廣播表;
Actual SQL: ds0 ::: select user_id,order_id from t_order0 where user_id = 102
3.屬於DMLStatement
select 2+2
此DMLStatement
沒有表名,並且需要指定默認數據源名稱;
Actual SQL: ds0 ::: select 2+2
ShardingUnicastRoutingEngine
單播路由用於獲取某一真實表信息的場景,它僅需要從任意庫中的任意真實表中獲取數據即可;
1.屬於DALStatement
desc t_order
日誌如下:
Actual SQL: ds1 ::: desc t_order0
2.邏輯表都屬於廣播表
廣播表分片數據源中都存在的表,一般是字典表:
select * from t_config
需要配置廣播表:
Collection<String> broadcastTables = new LinkedList<>();
broadcastTables.add("t_config");
shardingRuleConfig.setBroadcastTables(broadcastTables);
日誌如下:
Actual SQL: ds0 ::: select * from t_config
3.屬於DMLStatement
屬於DMLStatement
同時分片條件爲AlwaysFalseShardingCondition
,或者沒有指定表名,或者沒有配置表規則;
select user_id,order_id from t_order where order_id = 101 and order_id = 102
where後面指定的條件會導致分片條件爲AlwaysFalseShardingCondition
Actual SQL: ds1 ::: select user_id,order_id from t_order0 where order_id = 101 and order_id = 102
ShardingDataSourceGroupBroadcastRoutingEngine
數據源組廣播,從數據源組中隨機選擇一個數據源;
1.屬於DALStatement
SHOW STATUS
DALStatement
子類中除了use、set、reset、show databases;基本都會走此引擎;
Actual SQL: ds1 ::: SHOW STATUS
ShardingMasterInstanceBroadcastRoutingEngine
全實例路由用於 DCL 操作,授權語句針對的是數據庫的實例。無論一個實例中包含多少個 Schema,每個數據庫的實例只執行一次;
1.屬於DCLStatement
CREATE USER [email protected] identified BY '123'
注:這裏的主實例會檢測各實例之間,不能有相同的hostname
和相同的port
,本地測試同一臺Mysql不同庫,配置hostname不一致即可,比如localhost和127.0.0.1;
ShardingStandardRoutingEngine
標準路由是最常見的分片方式了,經過以上幾種路由引擎的過濾,剩下的SQLStatement
,就會走剩下的兩個引擎了,我們配置分庫分表策略,常規使用的增刪改查都會使用此引擎;
1.單表查詢
select user_id,order_id from t_order where order_id = 101
以上使用常規配置,兩個數據源分別是ds0,ds1;user_id作爲分庫鍵,order_id作爲分表鍵;
Actual SQL: ds0 ::: select user_id,order_id from t_order1 where order_id = 101
Actual SQL: ds1 ::: select user_id,order_id from t_order1 where order_id = 101
101經過分片算法定位到物理表t_order1
,但是無法定位數據庫,所以分別到兩個庫執行;
2.關聯查詢
select a.user_id,a.order_id from t_order a left join t_order_item b ON a.order_id=b.order_id where a.order_id = 101
如果是關聯查詢,則只有在兩者配置了綁定關係,纔會使用標準路由;
Collection<String> bindingTables = new LinkedList<>();
bindingTables.add("t_order,t_order_item");
shardingRuleConfig.setBindingTableGroups(bindingTables);
以上配置了兩張表爲綁定表,關聯查詢與單表查詢複雜度和性能相當,不會進行笛卡爾路由;
Actual SQL: ds0 ::: select a.user_id,a.order_id from t_order1 a left join t_order1 b ON a.order_id=b.order_id where a.order_id = 101
Actual SQL: ds1 ::: select a.user_id,a.order_id from t_order1 a left join t_order1 b ON a.order_id=b.order_id where a.order_id = 101
ShardingComplexRoutingEngine
笛卡爾路由是最複雜的情況,它無法根據綁定表的關係定位分片規則,因此非綁定表之間的關聯查詢需要拆解爲笛卡爾積組合執行;
1.關聯查詢
以上SQL如果不配置綁定關係,那麼會進行笛卡爾路由,路由日誌如下:
Actual SQL: ds0 ::: select a.user_id,a.order_id from t_order1 a left join t_order_item0 b ON a.order_id=b.order_id where a.order_id = 101
Actual SQL: ds0 ::: select a.user_id,a.order_id from t_order1 a left join t_order_item1 b ON a.order_id=b.order_id where a.order_id = 101
Actual SQL: ds1 ::: select a.user_id,a.order_id from t_order1 a left join t_order_item0 b ON a.order_id=b.order_id where a.order_id = 101
Actual SQL: ds1 ::: select a.user_id,a.order_id from t_order1 a left join t_order_item1 b ON a.order_id=b.order_id where a.order_id = 101
執行路由處理
經過以上流程處理,已經獲取到了處理此SQLStatement
對應的路由引擎,接下來只需要執行對應的路由引擎,獲取路由結果即可;
public interface ShardingRouteEngine {
RouteResult route(ShardingRule shardingRule);
}
傳入分片規則ShardingRule
,返回路由結果RouteResult
;下面以標準路由爲例,來分析是如何執行路由處理的;ShardingStandardRoutingEngine
的核心方法getDataNodes
如下所示:
private Collection<DataNode> getDataNodes(final ShardingRule shardingRule, final TableRule tableRule) {
if (isRoutingByHint(shardingRule, tableRule)) {
return routeByHint(shardingRule, tableRule);
}
if (isRoutingByShardingConditions(shardingRule, tableRule)) {
return routeByShardingConditions(shardingRule, tableRule);
}
return routeByMixedConditions(shardingRule, tableRule);
}
以上有三種路由方式:hint方式路由、分片條件路由、混合條件路由;下面分別介紹;
Hint方式路由
首先判斷是否使用強制路由方式:
private boolean isRoutingByHint(final ShardingRule shardingRule, final TableRule tableRule) {
return shardingRule.getDatabaseShardingStrategy(tableRule) instanceof HintShardingStrategy && shardingRule.getTableShardingStrategy(tableRule) instanceof HintShardingStrategy;
}
需要庫和表路由策略都是HintShardingStrategy
,這個只需要在配置TableRuleConfiguration
分別配置數據庫和表的策略都爲HintShardingStrategyConfiguration
即可;
private Collection<DataNode> route0(final ShardingRule shardingRule, final TableRule tableRule, final List<RouteValue> databaseShardingValues, final List<RouteValue> tableShardingValues) {
Collection<String> routedDataSources = routeDataSources(shardingRule, tableRule, databaseShardingValues);
Collection<DataNode> result = new LinkedList<>();
for (String each : routedDataSources) {
result.addAll(routeTables(shardingRule, tableRule, each, tableShardingValues));
}
return result;
}
接下來就是分別執行路由庫和路由表,不管是路由庫還是表都需要兩個核心的參數:當前可用的目標庫或表、當前的分片值;
- 當前可用的目標庫或表:這個就是初始化的庫和表,比如目標庫包括ds0、ds1,目標表包括t_order0、t_order1;
- 當前的分片值:當前的hint方式就是通過
HintManager
配置的分片值;
其他兩種方式其實路由方式也都類似,只是分片值獲取的方式不一樣;有了這兩個值就會調用我們自己定義的分庫分表算法ShardingAlgorithm
,這樣就返回了經過路由後的庫表,將結果保存到DataNode
中:
public final class DataNode {
private final String dataSourceName;
private final String tableName;
}
一個真實的庫對應一個真實的表;最後將DataNode
封裝到RouteResult
即可;
分片條件路由
同樣首先判斷是否走分片條件路由:
private boolean isRoutingByShardingConditions(final ShardingRule shardingRule, final TableRule tableRule) {
return !(shardingRule.getDatabaseShardingStrategy(tableRule) instanceof HintShardingStrategy || shardingRule.getTableShardingStrategy(tableRule) instanceof HintShardingStrategy);
}
庫和表路由策略都不是HintShardingStrategy
的情況下才會走分片條件路由;
private Collection<DataNode> routeByShardingConditions(final ShardingRule shardingRule, final TableRule tableRule) {
return shardingConditions.getConditions().isEmpty()
? route0(shardingRule, tableRule, Collections.emptyList(), Collections.emptyList()) : routeByShardingConditionsWithCondition(shardingRule, tableRule);
}
然後會判斷是否有ShardingConditions
,關於ShardingConditions
上面章節會專門介紹;如果沒有ShardingConditions
說明沒有條件就會走全庫表路由,如果有的話會從ShardingConditions
中取出庫表分片值,下面的邏輯就和Hint方式一樣了;
混合條件路由
混合模式就是庫和表路由策略都不全都是HintShardingStrategy
,要麼表使用強制路由,要麼庫使用強制路由;
private Collection<DataNode> routeByMixedConditions(final ShardingRule shardingRule, final TableRule tableRule) {
return shardingConditions.getConditions().isEmpty() ? routeByMixedConditionsWithHint(shardingRule, tableRule) : routeByMixedConditionsWithCondition(shardingRule, tableRule);
}
會判斷是否有ShardingConditions
,如果沒有說明庫或者表路由有一個使用HintShardingStrategy
,另外一個沒有;否則就是Hint和Condition混合;這種情況就要看誰的優先級高了,很明顯是Hint方式優先級高,可用看庫分片值獲取:
private List<RouteValue> getDatabaseShardingValues(final ShardingRule shardingRule, final TableRule tableRule, final ShardingCondition shardingCondition) {
ShardingStrategy dataBaseShardingStrategy = shardingRule.getDatabaseShardingStrategy(tableRule);
return isGettingShardingValuesFromHint(dataBaseShardingStrategy)
? getDatabaseShardingValuesFromHint() : getShardingValuesFromShardingConditions(shardingRule, dataBaseShardingStrategy.getShardingColumns(), shardingCondition);
}
如果能獲取到Hint分片值,那就使用Hint值,否則就從Condition
中獲取;
讀寫分離路由裝飾器
經過上面分片路由裝飾器的處理,根據優先級,如果配置了讀寫分離會執行讀寫分離裝飾器MasterSlaveRouteDecorator
;大致流程如下所示:
讀寫分離配置
List<String> slaveDataSourceNames0 = new ArrayList<String>();
slaveDataSourceNames0.add("ds01");
MasterSlaveRuleConfiguration masterSlaveRuleConfiguration0 = new MasterSlaveRuleConfiguration("ds0", "ds0",
slaveDataSourceNames0);
shardingRuleConfig.getMasterSlaveRuleConfigs().add(masterSlaveRuleConfiguration0);
List<String> slaveDataSourceNames1 = new ArrayList<String>();
slaveDataSourceNames1.add("ds11");
MasterSlaveRuleConfiguration masterSlaveRuleConfiguration1 = new MasterSlaveRuleConfiguration("ds1", "ds1",
slaveDataSourceNames1);
shardingRuleConfig.getMasterSlaveRuleConfigs().add(masterSlaveRuleConfiguration1);
首先必須配置了讀寫分離策略ds0備庫爲ds01,ds1備庫爲ds11;
庫名稱匹配
經過分片路由生成的RouteUnit
中對應的庫名稱,和MasterSlaveRule
中配置的名稱能匹配;
讀寫路由
public String route(final SQLStatement sqlStatement) {
if (isMasterRoute(sqlStatement)) {
MasterVisitedManager.setMasterVisited();
return masterSlaveRule.getMasterDataSourceName();
}
return masterSlaveRule.getLoadBalanceAlgorithm().getDataSource(
masterSlaveRule.getName(), masterSlaveRule.getMasterDataSourceName(), new ArrayList<>(masterSlaveRule.getSlaveDataSourceNames()));
}
並不是配置了讀寫分離都會進行路由處理,有些SQLStatement
是必須走主表的:
private boolean isMasterRoute(final SQLStatement sqlStatement) {
return containsLockSegment(sqlStatement) || !(sqlStatement instanceof SelectStatement) || MasterVisitedManager.isMasterVisited() || HintManager.isMasterRouteOnly();
}
private boolean containsLockSegment(final SQLStatement sqlStatement) {
return sqlStatement instanceof SelectStatement && ((SelectStatement) sqlStatement).getLock().isPresent();
}
SelectStatement
中包含了鎖- 非
SelectStatement
- 配置了
MasterVisitedManager
,內部使用ThreadLocal管理 - 配置了
HintManager
,內部使用ThreadLocal管理
以上四種情況都是走主表,其他情況走備庫,如果有多臺備庫,會進行負載均衡處理;
替換路由庫
經過讀寫分離路由處理之後,獲取到庫名稱需要替換原理的庫名稱;
總結
本文主要介紹了ShardingSphere-JDBC
的分片路由引擎,重點和難點在於不同類型的SQLStatement
使用不同的路由引擎,路由引擎衆多,本文重點舉例介紹了每種路由引擎在何種條件下使用;經過路由引擎會獲取到路由結果下面就是對SQL進行改寫了,改寫成真實的庫表,這樣數據庫才能執行。
參考
https://shardingsphere.apache.org/document/current/cn/overview/
感謝關注
可以關注微信公衆號「回滾吧代碼」,第一時間閱讀,文章持續更新;專注Java源碼、架構、算法和麪試。