前言
本章继续分析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 = 0
、commit
、roolback
等,走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 tables
、describe 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 = 6
或select * 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);
}
回到ShardingRouteEngineFactory
,getShardingRoutingEngine
方法是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
ComplexShardingStrategy
与StandardShardingStrategy
相比,它是针对多字段的分片策略。
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才会生效,一共有四种策略。