sharding-jdbc路由分析

寵の児 提交于 2019-11-29 06:20:19

路由引擎主要分为两大类:

  • 分片路由(直接路由、标准路由、笛卡尔积路由)

  • 广播路由(全库表路由、全库路由、全实例路由、单播路由、阻断路由)

具体路由类型含义参考官网路由引擎

https://shardingsphere.apache.org/document/current/cn/features/sharding/principle/route/

主要分析查询路由

1.路由ParsingSQLRouter#route入口

@RequiredArgsConstructor  public final class ParsingSQLRouter implements ShardingRouter {      @Override      public SQLRouteResult route(final SQLStatement sqlStatement, final List<Object> parameters) {          //优化,处理条件占位符参数与真实数据、分页、group by etc.          OptimizedStatement optimizedStatement = OptimizeEngineFactory.newInstance(shardingRule, shardingMetaData.getTable(), sqlStatement, parameters).optimize();          boolean needMergeShardingValues = isNeedMergeShardingValues(sqlStatement);          if (optimizedStatement instanceof ShardingWhereOptimizedStatement && needMergeShardingValues) {              checkSubqueryShardingValues(sqlStatement, ((ShardingWhereOptimizedStatement) optimizedStatement).getShardingConditions());              mergeShardingConditions(((ShardingWhereOptimizedStatement) optimizedStatement).getShardingConditions());          }          //路由入口          RoutingResult routingResult = RoutingEngineFactory.newInstance(shardingRule, shardingMetaData.getDataSource(), optimizedStatement).route();          if (needMergeShardingValues) {              Preconditions.checkState(1 == routingResult.getRoutingUnits().size(), "Must have one sharding with subquery.");          }          if (optimizedStatement instanceof ShardingInsertOptimizedStatement) {              setGeneratedValues((ShardingInsertOptimizedStatement) optimizedStatement);          }          SQLRouteResult result = new SQLRouteResult(optimizedStatement);          result.setRoutingResult(routingResult);          return result;      }      ... ...  }

 

2.路由工厂并路由RoutingEngineFactory#route

@NoArgsConstructor(access = AccessLevel.PRIVATE)  public final class RoutingEngineFactory {            /**       * Create new instance of routing engine.       *        * @param shardingRule sharding rule       * @param shardingDataSourceMetaData sharding data source meta data       * @param optimizedStatement optimized statement       * @return new instance of routing engine       */      public static RoutingEngine newInstance(final ShardingRule shardingRule, final ShardingDataSourceMetaData shardingDataSourceMetaData, final OptimizedStatement optimizedStatement) {          SQLStatement sqlStatement = optimizedStatement.getSQLStatement();          Collection<String> tableNames = sqlStatement.getTables().getTableNames();          //全库路由          if (sqlStatement instanceof TCLStatement) {              return new DatabaseBroadcastRoutingEngine(shardingRule);          }          //全库表路由          if (sqlStatement instanceof DDLStatement) {              return new TableBroadcastRoutingEngine(shardingRule, optimizedStatement);          }          //阻断路由          if (sqlStatement instanceof DALStatement) {              return getDALRoutingEngine(shardingRule, sqlStatement, tableNames);          }          //全实例路由          if (sqlStatement instanceof DCLStatement) {              return getDCLRoutingEngine(shardingRule, optimizedStatement, shardingDataSourceMetaData);          }          //默认库路由          if (shardingRule.isAllInDefaultDataSource(tableNames)) {              return new DefaultDatabaseRoutingEngine(shardingRule, tableNames);          }          //全库路由          if (shardingRule.isAllBroadcastTables(tableNames)) {              return sqlStatement instanceof SelectStatement ? new UnicastRoutingEngine(shardingRule, tableNames) : new DatabaseBroadcastRoutingEngine(shardingRule);          }          //单播路由          if (optimizedStatement instanceof ShardingWhereOptimizedStatement && ((ShardingWhereOptimizedStatement) optimizedStatement).getShardingConditions().isAlwaysFalse() || tableNames.isEmpty()) {              return new UnicastRoutingEngine(shardingRule, tableNames);          }          Preconditions.checkState(optimizedStatement instanceof ShardingWhereOptimizedStatement);          //分片路由          return getShardingRoutingEngine(shardingRule, (ShardingWhereOptimizedStatement) optimizedStatement, tableNames);      }      ... ...      private static RoutingEngine getShardingRoutingEngine(final ShardingRule shardingRule, final ShardingWhereOptimizedStatement optimizedStatement, final Collection<String> tableNames) {          ///根据解析出来逻辑表获取分片表,如:SELECT i.* FROM t_order o, t_order_item i WHERE o.order_id = i.order_id and o.order_id = ?          //则shardingTableNames为t_order,t_order_item          Collection<String> shardingTableNames = shardingRule.getShardingLogicTableNames(tableNames);          //满足以下两个条件走标准路由,否则走复合路由          //1、是否只有一张分片表          //2、绑定的逻辑表(配置绑定表)是否包含所有分片表          if (1 == shardingTableNames.size() || shardingRule.isAllBindingTables(shardingTableNames)) {              //标准路由,获取第一张表路由即可,参考官网案例说明              return new StandardRoutingEngine(shardingRule, shardingTableNames.iterator().next(), optimizedStatement);          }          // TODO config for cartesian set          //复合路由          return new ComplexRoutingEngine(shardingRule, tableNames, optimizedStatement);      }  }

 

3.标准路由StandardRoutingEngine#route

标准路由场景

  • t_order和t_order_item是分库分表并且是绑定表;如第二步55行判断shardingTableNames.size()=1或者shardingTableNames都是绑定表时,这时会走标准路由
@RequiredArgsConstructor  public final class StandardRoutingEngine implements RoutingEngine {  ​      //分库分表规则      private final ShardingRule shardingRule;      //逻辑表 t_order      private final String logicTableName;      //sql解析并优化后的结果      private final ShardingWhereOptimizedStatement optimizedStatement;            @Override      public RoutingResult route() {          //insert、update、delete判断表是否是单表          if (isDMLForModify(optimizedStatement.getSQLStatement()) && !optimizedStatement.getSQLStatement().getTables().isSingleTable()) {              throw new SQLParsingException("Cannot support Multiple-Table for '%s'.", optimizedStatement.getSQLStatement());          }          //路由数据节点、封装路由结果          return generateRoutingResult(getDataNodes(shardingRule.getTableRule(logicTableName)));      }           ... ...                private RoutingResult generateRoutingResult(final Collection<DataNode> routedDataNodes) {          RoutingResult result = new RoutingResult();          //根据数据节点封装路由单元、表单元          for (DataNode each : routedDataNodes) {              //路由单元 demo_ds_0              RoutingUnit routingUnit = new RoutingUnit(each.getDataSourceName());              //表单元 逻辑表:真实表 t_order:t_order_0              routingUnit.getTableUnits().add(new TableUnit(logicTableName, each.getTableName()));              result.getRoutingUnits().add(routingUnit);          }          return result;      }            private Collection<DataNode> getDataNodes(final TableRule tableRule) {          //判断database、table分片策略同时是Hint(直接路由)          if (shardingRule.isRoutingByHint(tableRule)) {              return routeByHint(tableRule);          }          //database、table分片策略都不是Hint          if (isRoutingByShardingConditions(tableRule)) {              //根据分片条件、策略路由到对应的database、table,同对分片键判断              return routeByShardingConditions(tableRule);          }          //database或table分片策略有一个是Hint          return routeByMixedConditions(tableRule);      }      ... ...  }

 

4.复合路由CartesianRoutingEngine#route

复杂路由场景

  • t_order和t_order_item是分库分表并且是绑定表;新增一个t_user分库分表,这时第二步的55行,shardingTableNames.size()=3,且t_user未配置成绑定表,这时会走复合路由
@RequiredArgsConstructor  public final class ComplexRoutingEngine implements RoutingEngine {      //分库分表规则      private final ShardingRule shardingRule;      //逻辑表t_order、t_order_item      private final Collection<String> logicTables;      //sql解析并优化后的结果      private final ShardingWhereOptimizedStatement optimizedStatement;            @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.findTableRule(each);              //表是否配置了分库分表规则              if (tableRule.isPresent()) {                  // 如果绑定关系表已经处理过,那么不需要再处理,如t_order处理过,由于t_order_item与其是绑定关系,那么不需要再处理;                  if (!bindingTableNames.contains(each)) {                      //构建标准路由并路由                      result.add(new StandardRoutingEngine(shardingRule, tableRule.get().getLogicTable(), optimizedStatement).route());                  }                  //根据逻辑表查找对应的所有绑定表,如根据t_order就能查询到t_order、t_order_item,因为t_order和t_order_item是绑定表                  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();                          }                      }));                  }              }          }          if (result.isEmpty()) {              throw new ShardingException("Cannot find table rule and default data source with logic tables: '%s'", logicTables);          }          if (1 == result.size()) {              return result.iterator().next();          }          //笛卡尔积路由          return new CartesianRoutingEngine(result).route();      }  }

 

5.笛卡尔积路由

笛卡尔积路由场景

  • 笛卡尔路由是最复杂的情况,它无法根据绑定表的关系定位分片规则,因此非绑定表之间的关联查询需要拆解为笛卡尔积组合执行。如上面事例新增的t_user分库分表,t_user和t_order未配置绑定表关系,如下SQL就需要走笛卡尔积路由
  • SELECT * FROM t_user u JOIN t_order o ON u.user_id = o.user_id AND u.user_id in(1 , 2)
@RequiredArgsConstructor  public final class CartesianRoutingEngine implements RoutingEngine {            private final Collection<RoutingResult> routingResults;            @Override      public RoutingResult route() {          RoutingResult result = new RoutingResult();          //获取数据源对应的逻辑表集合          for (Entry<String, Set<String>> entry : getDataSourceLogicTablesMap().entrySet()) {              //通过数据源名称和逻辑表的名称获取实际的表组,即[["t_user_0","t_user_1"],["t_order_0", "t_order_1]]              List<Set<String>> actualTableGroups = getActualTableGroups(entry.getKey(), entry.getValue());              //把逻辑表名封装成TableUnit表单元              //TableUnit包含数据源名称、逻辑表名、实际表名(通过这三个属性可以确定最终访问的表)              List<Set<TableUnit>> routingTableGroups = toRoutingTableGroups(entry.getKey(), actualTableGroups);              //封装RoutingUnit路由单元              //cartesianProduct计算笛卡尔积              result.getRoutingUnits().addAll(getRoutingUnits(entry.getKey(), Sets.cartesianProduct(routingTableGroups)));          }          return result;      }            private Map<String, Set<String>> getDataSourceLogicTablesMap() {          //获取数据源的交集,如t_user逻辑表路由到数据源demo_ds_0,而t_order表路由到数据源ds_demo_0和demo_ds_1,数据源交集就是demo_ds_0          //事例SELECT * FROM t_user_0 u JOIN t_order_0 o ON u.user_id = o.user_id WHERE u.user_id in(1, 2); t_user和t_order不是绑定表关系          //笛卡尔路由是最复杂的情况,它无法根据绑定表的关系定位分片规则,因此非绑定表之间的关联查询需要拆解为笛卡尔积组合执行          Collection<String> intersectionDataSources = getIntersectionDataSources();          Map<String, Set<String>> result = new HashMap<>(routingResults.size());          //遍历标准路由后的结果集          for (RoutingResult each : routingResults) {              //通过数据源的名称获取数据源和逻辑表之间的映射关系              for (Entry<String, Set<String>> entry : each.getDataSourceLogicTablesMap(intersectionDataSources).entrySet()) {                  if (result.containsKey(entry.getKey())) {                      result.get(entry.getKey()).addAll(entry.getValue());                  } else {                      result.put(entry.getKey(), entry.getValue());                  }              }          }          // 返回数据源-逻辑表集合组成的Map,这里就是{"demo_ds_0":["t_user", "t_order"]}          return result;      }            private Collection<String> getIntersectionDataSources() {          Collection<String> result = new HashSet<>();          for (RoutingResult each : routingResults) {              if (result.isEmpty()) {                  result.addAll(each.getDataSourceNames());              }  ​              //交集              result.retainAll(each.getDataSourceNames());          }          return result;      }      ... ...  }

笛卡尔积结果如下:

 

6.直接路由

直接路由场景

  • 满足直接路由的条件相对苛刻,它需要通过Hint(使用HintAPI直接指定路由至库表)方式分片,并且是只分库不分表的前提下,则可以避免SQL解析和之后的结果归并
  • 假如路由算法为value % 2,当一个逻辑库t_order对应2个真实库t_order_0和t_order_1时,路由后SQL将在t_order_1上执行。下方是使用API的代码样例:
    String sql = "SELECT * FROM t_order";      try (      //获取Hint实例      HintManager hintManager = HintManager.getInstance();      Connection conn = dataSource.getConnection();      PreparedStatement pstmt = conn.prepareStatement(sql)) {          //设置数据源分片个数          hintManager.setDatabaseShardingValue(3);          try (ResultSet rs = pstmt.executeQuery()) {              while (rs.next()) {                  //...              }          }      }
@RequiredArgsConstructor  public final class DatabaseHintRoutingEngine implements RoutingEngine {      //数据源集群      private final Collection<String> dataSourceNames;      //Hint数据分片策略      private final HintShardingStrategy databaseShardingStrategy;            @Override      public RoutingResult route() {           //获取当前线程数据源分片          Collection<Comparable<?>> shardingValues = HintManager.getDatabaseShardingValues();          Preconditions.checkState(!shardingValues.isEmpty());          Collection<String> routingDataSources;          //根据分片策略路由          routingDataSources = databaseShardingStrategy.doSharding(dataSourceNames, Collections.<RouteValue>singletonList(new ListRouteValue<>("", "", shardingValues)));          Preconditions.checkState(!routingDataSources.isEmpty(), "no database route info");          RoutingResult result = new RoutingResult();          //封装路由单元          for (String each : routingDataSources) {              result.getRoutingUnits().add(new RoutingUnit(each));          }          return result;      }  }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!