ShardingJDBC源码阅读(五)路由(下)

2,025 阅读8分钟

前言

本章继续分析ShardingJDBC的核心步骤:路由

  • ShardingRouteEngineFactory路由引擎工厂。
  • ShardingStrategy分片策略。

一、ShardingRouteEngineFactory路由引擎工厂

1、回顾ShardingRouteDecorator

public final class ShardingRouteDecorator implements RouteDecorator<ShardingRule> {
  @Override
  public RouteContext decorate(final RouteContext routeContext, final ShardingSphereMetaData metaData, final ShardingRule shardingRule, final ConfigurationProperties properties) {
      // SQL上下文 SQLParserEngine解析SQL DataNodeRouter创建
      SQLStatementContext sqlStatementContext = routeContext.getSqlStatementContext();
      // 参数列表
      List<Object> parameters = routeContext.getParameters();
      // 对于SQL做校验
      ShardingStatementValidatorFactory.newInstance(
              sqlStatementContext.getSqlStatement()).ifPresent(validator -> validator.validate(shardingRule, sqlStatementContext.getSqlStatement(), parameters));
      // 创建ShardingConditions 包含很多RouteValue 用于route
      ShardingConditions shardingConditions = getShardingConditions(parameters, sqlStatementContext, metaData.getSchema(), shardingRule);
      // 合并shardingConditions
      boolean needMergeShardingValues = isNeedMergeShardingValues(sqlStatementContext, shardingRule);
      if (sqlStatementContext.getSqlStatement() instanceof DMLStatement && needMergeShardingValues) {
          checkSubqueryShardingValues(sqlStatementContext, shardingRule, shardingConditions);
          mergeShardingConditions(shardingConditions);
      }
      // 路由
      ShardingRouteEngine shardingRouteEngine = ShardingRouteEngineFactory.newInstance(shardingRule, metaData, sqlStatementContext, shardingConditions, properties);
      RouteResult routeResult = shardingRouteEngine.route(shardingRule);
      if (needMergeShardingValues) {
          Preconditions.checkState(1 == routeResult.getRouteUnits().size(), "Must have one sharding with subquery.");
      }
      // 把路由结果放入上下文
      return new RouteContext(sqlStatementContext, parameters, routeResult);
  }
} 

ShardingRouteDecorator的decorate方法是路由的核心流程。

  • 根据sql获取ShardingConditions
  • ShardingRouteEngineFactory获取路由引擎
  • ShardingRouteEngine路由引擎执行,得到RouteResult

ShardingRouteEngine的各种实现在上一章已经了解了,目前未知的是,哪种sql走哪种引擎,这是ShardingRouteEngineFactory根据多种因素决定的。

2、ShardingRouteEngineFactory

ShardingRouteEngineFactory作为ShardingRouteEngine工厂,根据入参构造不同的ShardingRouteEngine返回给客户端。重点关注他的唯一public方法newInstance

public static ShardingRouteEngine newInstance(final ShardingRule shardingRule,
                                                  final ShardingSphereMetaData metaData, final SQLStatementContext sqlStatementContext,
                                                  final ShardingConditions shardingConditions, final ConfigurationProperties properties) {
  SQLStatement sqlStatement = sqlStatementContext.getSqlStatement();
  Collection<String> tableNames = sqlStatementContext.getTablesContext().getTableNames();
  if (sqlStatement instanceof TCLStatement) {
      return new ShardingDatabaseBroadcastRoutingEngine();
  }
  if (sqlStatement instanceof DDLStatement) {
      return new ShardingTableBroadcastRoutingEngine(metaData.getSchema(), sqlStatementContext);
  }
  if (sqlStatement instanceof DALStatement) {
      return getDALRoutingEngine(shardingRule, sqlStatement, tableNames);
  }
  if (sqlStatement instanceof DCLStatement) {
      return getDCLRoutingEngine(sqlStatementContext, metaData);
  }
  if (shardingRule.isAllInDefaultDataSource(tableNames)) {
      return new ShardingDefaultDatabaseRoutingEngine(tableNames);
  }
  if (shardingRule.isAllBroadcastTables(tableNames)) {
      return sqlStatement instanceof SelectStatement ? new ShardingUnicastRoutingEngine(tableNames) : new ShardingDatabaseBroadcastRoutingEngine();
  }
  if (sqlStatementContext.getSqlStatement() instanceof DMLStatement && tableNames.isEmpty() && shardingRule.hasDefaultDataSourceName()) {
      return new ShardingDefaultDatabaseRoutingEngine(tableNames);
  }
  if (sqlStatementContext.getSqlStatement() instanceof DMLStatement && shardingConditions.isAlwaysFalse() || tableNames.isEmpty() || !shardingRule.tableRuleExists(tableNames)) {
      return new ShardingUnicastRoutingEngine(tableNames);
  }
  return getShardingRoutingEngine(shardingRule, sqlStatementContext, shardingConditions, tableNames, properties);
}

TCLStatement

事务sql,如set autocommit = 0commitroolback等,走ShardingDatabaseBroadcastRoutingEngine数据源广播。

if (sqlStatement instanceof TCLStatement) {
    return new ShardingDatabaseBroadcastRoutingEngine();
}

值得注意的是,一般我们开启事务和提交事务都不会直接通过sql来直接操作,一般都是通过connection.setAutoCommit(false)设置非自动提交,然后通过connection.commit提交事务。

正常使用jdbc时,connection.setAutoCommit(false)set autocommit = 0是没有区别的,因为对于com.mysql.jdbc.ConnectionImpl#setAutoCommit,最终还是执行qlset autocommit = 0

但是使用sharding-jdbc时,connection.setAutoCommit(false)可以避免多余的数据源执行(后续讲事务的时候再说,提一下是用了WrapperAdapter#replayMethodsInvocation方法),而执行sqlset autocommit = 0会导致数据源广播执行。

DDLStatement

DDL,如alter table t_order modify column status varchar(255) DEFAULT NULL,会执行ShardingTableBroadcastRoutingEngine表广播。

if (sqlStatement instanceof DDLStatement) {
    return new ShardingTableBroadcastRoutingEngine(metaData.getSchema(), sqlStatementContext);
}

DALStatement

DAL,如set @xxx = ?show tablesdescribe t_order

private static ShardingRouteEngine getDALRoutingEngine(final ShardingRule shardingRule, final SQLStatement sqlStatement, final Collection<String> tableNames) {
    if (sqlStatement instanceof UseStatement) {
        return new ShardingIgnoreRoutingEngine();
    }
    // 数据源广播
    // set @xxx = ?; show databases;
    if (sqlStatement instanceof SetStatement || sqlStatement instanceof ResetParameterStatement || sqlStatement instanceof ShowDatabasesStatement) {
        return new ShardingDatabaseBroadcastRoutingEngine();
    }
    // 不存在tableRule 且 有默认数据源 默认数据源
    if (!tableNames.isEmpty() && !shardingRule.tableRuleExists(tableNames) && shardingRule.hasDefaultDataSourceName()) {
        return new ShardingDefaultDatabaseRoutingEngine(tableNames);
    }
    // 存在TableRule 或 有默认数据源 数据源单播
    // describe t_order
    if (!tableNames.isEmpty()) {
        return new ShardingUnicastRoutingEngine(tableNames);
    }
    // tableNames为空 数据源组播
    // show tables
    return new ShardingDataSourceGroupBroadcastRoutingEngine();
}
  • set @xxx=?show databases,走ShardingDatabaseBroadcastRoutingEngine数据源广播。
  • tableNames不为空 且 不存在tableRule 且 有默认数据源,走ShardingDefaultDatabaseRoutingEngine默认数据源。
  • 其他tableNames不为空的场景,如describe t_order,走ShardingUnicastRoutingEngine单播。
  • tableNames为空,如show tables,走ShardingDataSourceGroupBroadcastRoutingEngine组播,支持通过默认数据源执行。

ShardingDataSourceGroupBroadcastRoutingEngine是DDLStatement的最后一种方案。早期版本,对于show tables这种命令是不支持默认数据源的,导致设置了默认数据源名称,仍然走随机数据源执行对应SQL(原来的兜底方案是ShardingUnicastRoutingEngine单播,所以是随机数据源)。

While using database,it will select a random database!!!

DCLStatement

DCLStatement,用户权限相关的sql,如grant授权。

private static ShardingRouteEngine getDCLRoutingEngine(final SQLStatementContext sqlStatementContext, final ShardingSphereMetaData metaData) {
  // 如果是针对单表的DCL 表广播 GRANT ALL ON t_order TO 'user2'@'%'
  if (isDCLForSingleTable(sqlStatementContext)) {
      return new ShardingTableBroadcastRoutingEngine(metaData.getSchema(), sqlStatementContext);
  }
  // 否则 实例广播 GRANT ALL ON *.* TO 'user2'@'%'
  else {
      return new ShardingMasterInstanceBroadcastRoutingEngine(metaData.getDataSources());
  }
}

针对单表DCL和非单表DCL区分ShardingRouteEngine,前者使用ShardingTableBroadcastRoutingEngine表广播;后者使用ShardingMasterInstanceBroadcastRoutingEngine实例广播。

DMLStatement

全默认数据源

当所有表没有配置TableRule,也非广播表时,会取ShardingDefaultDatabaseRoutingEngine默认数据源路由引擎。

 if (shardingRule.isAllInDefaultDataSource(tableNames)) {
    return new ShardingDefaultDatabaseRoutingEngine(tableNames);
}

ShardingRule#isAllInDefaultDataSource判断逻辑表是否都在默认数据源中。

 public boolean isAllInDefaultDataSource(final Collection<String> logicTableNames) {
  if (!hasDefaultDataSourceName()) {
      return false;
  }
  for (String each : logicTableNames) {
      // 逻辑表 如果有表规则 或 是广播表 则 非默认数据源
      if (findTableRule(each).isPresent() || isBroadcastTable(each)) {
          return false;
      }
  }
  return !logicTableNames.isEmpty();
}

全广播表

当所有逻辑表都是广播表时,分两种情况。

  • select语句,执行ShardingUnicastRoutingEngine单播。
  • 非select语句,执行ShardingDatabaseBroadcastRoutingEngine数据源广播。
if (shardingRule.isAllBroadcastTables(tableNames)) {
  return sqlStatement instanceof SelectStatement 
           ? new ShardingUnicastRoutingEngine(tableNames) 
           : new ShardingDatabaseBroadcastRoutingEngine();
}

其他情况

如果表名列表为空(比如select now()等)且 配置了默认数据源,那么走ShardingDefaultDatabaseRoutingEngine默认数据源。

if (sqlStatementContext.getSqlStatement() instanceof DMLStatement 
      && tableNames.isEmpty() 
      && shardingRule.hasDefaultDataSourceName()) {
    return new ShardingDefaultDatabaseRoutingEngine(tableNames);
}

如果shardingConditions是isAlwaysFalse,或tableNames空,或tableNames没有对应的TableRule,走ShardingUnicastRoutingEngine单播。

if (sqlStatementContext.getSqlStatement() instanceof DMLStatement && shardingConditions.isAlwaysFalse() 
			|| tableNames.isEmpty() 
			|| !shardingRule.tableRuleExists(tableNames)) {
    return new ShardingUnicastRoutingEngine(tableNames);
}

那么什么时候shardingConditions是isAlwaysFalse呢?

前面ShardingRouteDecorator#getShardingConditions构造ShardingConditions时,会合并where条件里的RouteValue,当合并等值条件或区间条件时,会返回AlwaysFalseRouteValue,对应的会转换为AlwaysFalseShardingCondition。比如select * from t_order where order_id = 5 and order_id = 6select * from t_order where order_id > 5 and order_id < 5

WhereClauseShardingConditionEngine#mergeRouteValues

private RouteValue mergeRouteValues(final Column column, final Collection<RouteValue> routeValues) {
  Collection<Comparable<?>> listValue = null;
  Range<Comparable<?>> rangeValue = null;
  for (RouteValue each : routeValues) {
      if (each instanceof ListRouteValue) {
          // 合并等值条件时,可能返回AlwaysFalseRouteValue
          listValue = mergeListRouteValues(((ListRouteValue) each).getValues(), listValue);
          if (listValue.isEmpty()) {
              return new AlwaysFalseRouteValue();
          }
      } else if (each instanceof RangeRouteValue) {
          try {
             // 合并区间报错,返回AlwaysFalseRouteValue
              rangeValue = mergeRangeRouteValues(((RangeRouteValue) each).getValueRange(), rangeValue);
          } catch (final IllegalArgumentException ex) {
              return new AlwaysFalseRouteValue();
          }
      }
  }
  if (null == listValue) {
      return new RangeRouteValue<>(column.getName(), column.getTableName(), rangeValue);
  }
  if (null == rangeValue) {
      return new ListRouteValue<>(column.getName(), column.getTableName(), listValue);
  }
  listValue = mergeListAndRangeRouteValues(listValue, rangeValue);
  return listValue.isEmpty() ? new AlwaysFalseRouteValue() : new ListRouteValue<>(column.getName(), column.getTableName(), listValue);
}

回到ShardingRouteEngineFactorygetShardingRoutingEngine方法是DML最后一个判断逻辑,一般业务sql都是走这个方法。

private static ShardingRouteEngine getShardingRoutingEngine(final ShardingRule shardingRule, final SQLStatementContext sqlStatementContext,
                                                                final ShardingConditions shardingConditions, final Collection<String> tableNames, final ConfigurationProperties properties) {
  // 根据sql中的tableName 过滤出 配置了TableRule的tableName
  Collection<String> shardingTableNames = shardingRule.getShardingLogicTableNames(tableNames);
  // 如果过滤出的表只有一个 或 这些表全在一个绑定规则里 走ShardingStandardRoutingEngine
  if (1 == shardingTableNames.size() || shardingRule.isAllBindingTables(shardingTableNames)) {
      return new ShardingStandardRoutingEngine(shardingTableNames.iterator().next(), sqlStatementContext, shardingConditions, properties);
  }
  // 否则走ShardingComplexRoutingEngine
  // TODO config for cartesian set
  return new ShardingComplexRoutingEngine(tableNames, sqlStatementContext, shardingConditions, properties);
}

从上面的代码看出,如果涉及关联查询,要考虑配置绑定表关系,否则会进入ShardingComplexRoutingEngine。而ShardingComplexRoutingEngine的逻辑上一章讲过,会循环所有table执行ShardingStandardRoutingEngine,如果最终RouteResult大于1,会导致进入ShardingCartesianRoutingEngine做笛卡尔积(见上一章)。

二、ShardingStrategy分片策略

ShardingStandardRoutingEngine标准路由引擎执行时,最重要的分片策略没有讲到,回顾一下ShardingStandardRoutingEngine#route0这个核心方法。

// 路由核心方法
private Collection<DataNode> route0(final ShardingRule shardingRule, final TableRule tableRule, final List<RouteValue> databaseShardingValues, final List<RouteValue> tableShardingValues) {
    // dataSource路由
    Collection<String> routedDataSources = routeDataSources(shardingRule, tableRule, databaseShardingValues);
    Collection<DataNode> result = new LinkedList<>();
    for (String each : routedDataSources) {
        // table路由 table+dataSource构造为DataNode
        Collection<DataNode> dataNodes = routeTables(shardingRule, tableRule, each, tableShardingValues);
        result.addAll(dataNodes);
    }
    return result;
}
// 数据源路由
private Collection<String> routeDataSources(final ShardingRule shardingRule, final TableRule tableRule, final List<RouteValue> databaseShardingValues) {
    // 如果没有RouteValue 返回所有数据源
    if (databaseShardingValues.isEmpty()) {
        return tableRule.getActualDatasourceNames();
    }
    // 获取分库策略
    ShardingStrategy databaseShardingStrategy = shardingRule.getDatabaseShardingStrategy(tableRule);
    // 执行分片算法 获取数据源名
    Collection<String> dataSources = databaseShardingStrategy.doSharding(tableRule.getActualDatasourceNames(), databaseShardingValues, this.properties);
    // 放入LinkedHashSet
    Collection<String> result = new LinkedHashSet<>(dataSources);
    // 路由结果必须非空
    Preconditions.checkState(!result.isEmpty(), "no database route info");
    // 路由结果不能超出实际数据源集合范围
    Preconditions.checkState(tableRule.getActualDatasourceNames().containsAll(result), 
            "Some routed data sources do not belong to configured data sources. routed data sources: `%s`, configured data sources: `%s`", result, tableRule.getActualDatasourceNames());
    return result;
}
// 表路由
private Collection<DataNode> routeTables(final ShardingRule shardingRule, final TableRule tableRule, final String routedDataSource, final List<RouteValue> tableShardingValues) {
    // 根据dataSource 找到这个dataSource下所有的表
    Collection<String> availableTargetTables = tableRule.getActualTableNames(routedDataSource);
    Collection<String> routedTables;
    // 如果RouteValue是空 返回当前dataSource下所有的表
    if (tableShardingValues.isEmpty()) {
        routedTables = new LinkedHashSet<>(availableTargetTables);
    }
    // 执行分片算法获取表结果集
    else {
        ShardingStrategy tableShardingStrategy = shardingRule.getTableShardingStrategy(tableRule);
        Collection<String> tables = tableShardingStrategy.doSharding(availableTargetTables, tableShardingValues, this.properties);
        routedTables = new LinkedHashSet<>(tables);
    }
    // 路由结果不能为空
    Preconditions.checkState(!routedTables.isEmpty(), "no table route info");
    // 组装DataNode
    Collection<DataNode> result = new LinkedList<>();
    for (String each : routedTables) {
        result.add(new DataNode(routedDataSource, each));
    }
    return result;
}

InlineShardingStrategy

InlineShardingStrategy是通过groovy表达式配置的分片策略,构造方法如下。

public final class InlineShardingStrategy implements ShardingStrategy {
    // 字段
    private final String shardingColumn;
    // groovy.lang.Closure
    private final Closure<?> closure;
    
    public InlineShardingStrategy(final InlineShardingStrategyConfiguration inlineShardingStrategyConfig) {
        Preconditions.checkNotNull(inlineShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
        Preconditions.checkNotNull(inlineShardingStrategyConfig.getAlgorithmExpression(), "Sharding algorithm expression cannot be null.");
        shardingColumn = inlineShardingStrategyConfig.getShardingColumn();
        String algorithmExpression = InlineExpressionParser.handlePlaceHolder(inlineShardingStrategyConfig.getAlgorithmExpression().trim());
        closure = new InlineExpressionParser(algorithmExpression).evaluateClosure();
    }
}

doSharding方法。

@Override
public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<RouteValue> shardingValues, final ConfigurationProperties properties) {
  RouteValue shardingValue = shardingValues.iterator().next();
  // RangeRouteValue区间分片的处理方式
  if (properties.<Boolean>getValue(ConfigurationPropertyKey.ALLOW_RANGE_QUERY_WITH_INLINE_SHARDING) && shardingValue instanceof RangeRouteValue) {
      return availableTargetNames;
  }
  Preconditions.checkState(shardingValue instanceof ListRouteValue, "Inline strategy cannot support this type sharding:" + shardingValue.toString());
  // ListRouteValue分片
  Collection<String> shardingResult = doSharding((ListRouteValue) shardingValue);
  Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
  for (String each : shardingResult) {
      if (availableTargetNames.contains(each)) {
          result.add(each);
      }
  }
  return result;
}

首先,shardingValue是RangeRouteValue区间时,如果allow.range.query.with.inline.sharding为true,则返回所有availableTargetNames;反之,抛出异常。默认配置为false,抛出异常,目的是防止用户忽视了隐含的性能问题。

For inline sharding strategy, user only can config precise algorithm via inline expression, and cannot config range algorithm. So throw exception when SQL contains range conditions(such as BETWEEN AND, <, >, <=, >=) may better than query all data nodes. If query all data nodes instead of explicit exception, user may missing the potential performance issue. When turn this parameter on, query all data nodes with inline sharding to be allowed instead of explicit exception.

接下来就是将ListRouteValue转换为PreciseShardingValue集合,执行分片逻辑。groovy看不懂就不说了。

// 执行分片
private Collection<String> doSharding(final ListRouteValue shardingValue) {
  Collection<String> result = new LinkedList<>();
  // ListRouteValue转换为PreciseShardingValue集合
  for (PreciseShardingValue<?> each : transferToPreciseShardingValues(shardingValue)) {
      // 执行分片逻辑
      result.add(execute(each));
  }
  return result;
}
// 将ListRouteValue平铺成PreciseShardingValue集合
private List<PreciseShardingValue> transferToPreciseShardingValues(final ListRouteValue<?> shardingValue) {
  List<PreciseShardingValue> result = new ArrayList<>(shardingValue.getValues().size());
  // 循环ListRouteValue的values 构造PreciseShardingValue
  for (Comparable<?> each : shardingValue.getValues()) {
      result.add(new PreciseShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), each));
  }
  return result;
}
// 执行分片逻辑
private String execute(final PreciseShardingValue shardingValue) {
  Closure<?> result = closure.rehydrate(new Expando(), null, null);
  result.setResolveStrategy(Closure.DELEGATE_ONLY);
  result.setProperty(shardingColumn, shardingValue.getValue());
  return result.call().toString();
}

StandardShardingStrategy

StandardShardingStrategy是最常用的分片策略,针对单字段配置精确分片和区间分片算法,其中区间分片算法非必须。

public final class StandardShardingStrategy implements ShardingStrategy {
    // 字段
    private final String shardingColumn;
    // 精确分片算法
    private final PreciseShardingAlgorithm preciseShardingAlgorithm;
    // 区间分片算法
    private final RangeShardingAlgorithm rangeShardingAlgorithm;
    
    public StandardShardingStrategy(final StandardShardingStrategyConfiguration standardShardingStrategyConfig) {
        Preconditions.checkNotNull(standardShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
        Preconditions.checkNotNull(standardShardingStrategyConfig.getPreciseShardingAlgorithm(), "precise sharding algorithm cannot be null.");
        shardingColumn = standardShardingStrategyConfig.getShardingColumn();
        preciseShardingAlgorithm = standardShardingStrategyConfig.getPreciseShardingAlgorithm();
        rangeShardingAlgorithm = standardShardingStrategyConfig.getRangeShardingAlgorithm();
    }
}

doSharding方法针对不同类型的RouteValue,走不同的分片逻辑。

@Override
public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<RouteValue> shardingValues, final ConfigurationProperties properties) {
    RouteValue shardingValue = shardingValues.iterator().next();
    Collection<String> shardingResult = shardingValue instanceof ListRouteValue
            ? doSharding(availableTargetNames, (ListRouteValue) shardingValue) 
            : doSharding(availableTargetNames, (RangeRouteValue) shardingValue);
    Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
    result.addAll(shardingResult);
    return result;
}

如果是ListRouteValue,和InlineShardingStrategy一样,平铺ListRouteValue的values组装成PreciseShardingValue,循环执行精确分片算法。

private Collection<String> doSharding(final Collection<String> availableTargetNames, final ListRouteValue<?> shardingValue) {
  Collection<String> result = new LinkedList<>();
  for (Comparable<?> each : shardingValue.getValues()) {
      PreciseShardingValue preciseShardingValue = new PreciseShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), each);
      String target = preciseShardingAlgorithm.doSharding(availableTargetNames, preciseShardingValue);
      if (null != target) {
          result.add(target);
      }
  }
  return result;
}

如果是RangeRouteValue,配置的区间分片算法为空,会报错;否则直接把RangeRouteValue转换为RangeShardingValue,执行算法的doSharding方法。

private Collection<String> doSharding(final Collection<String> availableTargetNames, final RangeRouteValue<?> shardingValue) {
  if (null == rangeShardingAlgorithm) {
      throw new UnsupportedOperationException("Cannot find range sharding strategy in sharding rule.");
  }
  return rangeShardingAlgorithm.doSharding(availableTargetNames, 
          new RangeShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), shardingValue.getValueRange()));
}

ComplexShardingStrategy

ComplexShardingStrategyStandardShardingStrategy相比,它是针对多字段的分片策略。

public final class ComplexShardingStrategy implements ShardingStrategy {
    
    @Getter
    private final Collection<String> shardingColumns;
    
    private final ComplexKeysShardingAlgorithm shardingAlgorithm;
    
    public ComplexShardingStrategy(final ComplexShardingStrategyConfiguration complexShardingStrategyConfig) {
        Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingColumns(), "Sharding columns cannot be null.");
        Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
        shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        shardingColumns.addAll(Splitter.on(",").trimResults().splitToList(complexShardingStrategyConfig.getShardingColumns()));
        shardingAlgorithm = complexShardingStrategyConfig.getShardingAlgorithm();
    }
    
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<RouteValue> shardingValues, final ConfigurationProperties properties) {
        //  字段 - 值集合
        Map<String, Collection<Comparable<?>>> columnShardingValues = new HashMap<>(shardingValues.size(), 1);
        // 字段 - Range
        Map<String, Range<Comparable<?>>> columnRangeValues = new HashMap<>(shardingValues.size(), 1);
        String logicTableName = "";
        for (RouteValue each : shardingValues) {
            if (each instanceof ListRouteValue) {
                columnShardingValues.put(each.getColumnName(), ((ListRouteValue) each).getValues());
            } else if (each instanceof RangeRouteValue) {
                columnRangeValues.put(each.getColumnName(), ((RangeRouteValue) each).getValueRange());
            }
            logicTableName = each.getTableName();
        }
        // ComplexKeysShardingValue
        ComplexKeysShardingValue complexKeysShardingValue = new ComplexKeysShardingValue(logicTableName, columnShardingValues, columnRangeValues);
        // 执行ComplexKeysShardingAlgorithm
        Collection<String> shardingResult = shardingAlgorithm.doSharding(availableTargetNames, complexKeysShardingValue);
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.addAll(shardingResult);
        return result;
    }
}

HintShardingStrategy

HintShardingStrategy暗示分片策略,不通过解析sql得到ShardingConditions来获取分片键值,而是通过用户操作HintManager来设置分片值(不包括分片字段,因为与sql无关)。

public final class HintShardingStrategy implements ShardingStrategy {
    
    @Getter
    private final Collection<String> shardingColumns;
    
    private final HintShardingAlgorithm shardingAlgorithm;
    
    public HintShardingStrategy(final HintShardingStrategyConfiguration hintShardingStrategyConfig) {
        Preconditions.checkNotNull(hintShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
        shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        shardingAlgorithm = hintShardingStrategyConfig.getShardingAlgorithm();
    }
    
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<RouteValue> shardingValues, final ConfigurationProperties properties) {
        ListRouteValue shardingValue = (ListRouteValue) shardingValues.iterator().next();
        // HintShardingValue
        HintShardingValue hintShardingValue = new HintShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), shardingValue.getValues());
        // HintShardingAlgorithm
        Collection<String> shardingResult = shardingAlgorithm.doSharding(availableTargetNames, hintShardingValue);
        Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        result.addAll(shardingResult);
        return result;
    }
}

总结

  • ShardingRouteEngineFactory#getShardingRoutingEngine一般业务sql都是走这个方法判断使用哪种路由引擎。如果sql中的表只匹配单个TableRule或匹配的TableRule都隶属于同一个绑定关系,会走ShardingStandardRoutingEngine;否则会走ShardingComplexRoutingEngine循环每个sql表,执行ShardingStandardRoutingEngine,最终的RouteResult结果集超过1个元素,会导致进入ShardingCartesianRoutingEngine笛卡尔积路由引擎。
  • 对于标准路由引擎ShardingStandardRoutingEngine,ShardingStrategy才会生效,一共有四种策略。