阿飛Javaer,轉載請註明原創出處,謝謝!
路由條件
ParsingSQLRouter.java中決定是簡單路由還是複雜路由的條件如下;
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中只有一張表,而是有分庫分表規則的表數量,例如下面這段構造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是否都屬於綁定表,例如下面這段構造ShardingRule的源碼,.bindingTableRules()裏的參數就是綁定表集合,這裏是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))))
. *** ***;
- 是否都在默認數據源中–shardingRule.isAllInDefaultDataSource(tableNames)
說明:sharding-jdbc判斷邏輯源碼如下,即只要在表規則集合中能夠匹配到邏輯表,就認爲不屬於默認數據源中(默認數據源不分庫分表),例如ShardingRule.builder().dataSourceRule(dataSourceRule).tableRules(Arrays.asList(orderTableRule, orderItemTableRule, userTableRule))
,根據tableRules參數可知,主要SQL中有t_user
,t_order
,t_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();
}
構造複雜路由
綜上分析,如果三個條件都不滿足就走複雜路由ComplexRoutingEngine,構造這種場景:
t_order和t_order_item分庫分表且綁定表關係,加入一個新的分庫分表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();
執行的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=?
構造的這個場景:tableNames.size()=3(三張表t_user,t_order,t_order_item都有分庫分表規則,所以值爲3),shardingRule.isAllBindingTables(tableNames)爲false(t_user表不屬於綁定表範圍);shardingRule.isAllInDefaultDataSource(tableNames)爲false(三張表都不屬於默認數據源中的表);所以這個SQL會走複雜路由的邏輯;
ComplexRoutingEngine
複雜路由引擎的核心邏輯就是拆分成多個簡單路由,然後求笛卡爾積,複雜路由核心源碼如下:
@RequiredArgsConstructor
@Slf4j
public final class ComplexRoutingEngine implements RoutingEngine {
// 分庫分表規則
private final ShardingRule shardingRule;
// SQL請求參數,豬油一個user_id的值爲10
private final List<Object> parameters;
// 邏輯表集合:t_order,t_order_item,t_user,三個邏輯表
private final Collection<String> logicTables;
// SQL解析結果
private final SQLStatement sqlStatement;
// 複雜路由的核心邏輯
@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);
// 如果遍歷的表配置了分庫分表規則
if (tableRule.isPresent()) {
// 如果綁定關係表已經處理過,那麼不需要再處理,例如t_order處理過,由於t_order_item與其是綁定關係,那麼不需要再處理
if (!bindingTableNames.contains(each)) {
// 根據當前遍歷的邏輯表構造一個簡單路由規則
result.add(new SimpleRoutingEngine(shardingRule, parameters, tableRule.get().getLogicTable(), sqlStatement).route());
}
// 根據當前邏輯表,查找其對應的所有綁定表,例如根據t_order就能夠查詢出t_order和t_order_item;假如配置了.bindingTableRules(***t_point, t_point_detail***),那麼,根據t_point能查詢出t_point和t_point_detail,其目的是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);
// 如果是複雜路由,但是路由結果爲空,那麼拋出異常
if (result.isEmpty()) {
throw new ShardingJdbcException("Cannot find table rule and default data source with logic tables: '%s'", logicTables);
}
// 如果結果的size爲1,那麼直接返回即可
if (1 == result.size()) {
return result.iterator().next();
}
// 對剛剛的路由結果集合計算笛卡爾積,就是最終複雜的路由結果
return new CartesianRoutingEngine(result).route();
}
}
由上面源碼分析可知,會分別對t_user和t_order構造簡單路由(t_order_item和t_order是綁定關係,二者取其一即可);
- t_user只分庫不分表(因爲構造TableRule時邏輯表和實際表一致),且請求參數爲user_id=10,所以t_user這個邏輯表的簡單路由結果爲:數據源ds_jdbc_0,實際表t_user;
- t_order分庫分表,且請求參數user_id被解析爲t_user的條件(笛卡爾積路由引擎會處理),所以t_order的簡單路由結果爲:數據源ds_jdbc_0和ds_jdbc_1,實際表t_order_0和t_order_1;
debug的result如下:
CartesianRoutingEngine
如上分析,求得簡單路由結果集後,求笛卡爾積就是複雜路由的最終路由結果,笛卡爾積路由引擎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()) {
// 根據數據源&邏輯表,得到實際表集合,即[["t_user"],["t_order_0","t_order_1"]]
List<Set<String>> actualTableGroups = getActualTableGroups(entry.getKey(), entry.getValue());
// 把邏輯表名封裝,TableUnit的屬性有:數據源名稱,邏輯表名,實際表名(這三個屬性才能確定最終訪問的表)
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;
}
// 得到數據源-邏輯表集合組成的Map
private Map<String, Set<String>> getDataSourceLogicTablesMap() {
// 這裏很關鍵,是得到數據源的交集(上面分析時t_user邏輯表路由到數據源ds_jdbc_0,而t_order表路由到數據源ds_jdbc_0和ds_jdbc_1,數據源交集就是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());
}
}
}
// 得到的最終結果爲數據源-邏輯表集合組成的Map,這裏就是{"ds_jdbc_0":["t_order", "t_user"]}
return result;
}
... ...
}
計算得到的笛卡爾積結果如下:
sql.show
結果如下,可以看到重寫後的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]