(1). 概述
在这里,要对:TableRule进行一个详细的剖析,为什么呢?因为:这个类承载着太多的业务.
比如:我想自定义主键生成策略,想要自定义分表分库策略怎么办?
剖析完:TableRule的内容后,就能找到答案了.
(2). TableRule类结构图
仍然,参考上一小节剖析的UML图.
(3). TableRule在什么时候进行初始化的?
TableRule的初始化是在:ShardingRule构建器中进行的.
private final Collection<TableRule> tableRules;
public ShardingRule(final ShardingRuleConfiguration shardingRuleConfig, final Collection<String> dataSourceNames) {
// ... ...
// ******************************************************
// 4.
// 4.1. 解析:ShardingRuleConfiguration.Collection<TableRuleConfiguration>
// 4.2. 转换成:Collection<TableRule>
// ******************************************************
tableRules = createTableRules(shardingRuleConfig);
// ... ...
}
(4). ShardingRule.createTableRules
private Collection<TableRule> createTableRules(final ShardingRuleConfiguration shardingRuleConfig) {
// 4.1 遍历:shardingRuleConfig.getTableRuleConfigs()
return shardingRuleConfig.getTableRuleConfigs()
.stream()
// 把TableRuleConfiguration转换成:TableRule
.map(each -> new TableRule(each, shardingDataSourceNames, getDefaultGenerateKeyColumn(shardingRuleConfig)))
.collect(Collectors.toList());
}
(5). TableRule
从上面分析,不难看出:TableRule是在:ShardingRule.createTableRules创建的.
这里主要是分析:TableRule对:ShardingStrategy/ShardingKeyGenerator有没有提供相应的扩展点,以供我们使用.
public TableRule(final TableRuleConfiguration tableRuleConfig, final ShardingDataSourceNames shardingDataSourceNames, final String defaultGenerateKeyColumn) {
// 逻辑表名称
logicTable = tableRuleConfig.getLogicTable().toLowerCase();
// 数据节点(这时,已经对表达式进行了解析)
List<String> dataNodes = new InlineExpressionParser(tableRuleConfig.getActualDataNodes()).splitAndEvaluate();
dataNodeIndexMap = new HashMap<>(dataNodes.size(), 1);
actualDataNodes = isEmptyDataNodes(dataNodes)
? generateDataNodes(tableRuleConfig.getLogicTable(), shardingDataSourceNames.getDataSourceNames()) : generateDataNodes(dataNodes, shardingDataSourceNames.getDataSourceNames());
actualTables = getActualTables();
// ************************************************************************************
// 6. 通过ShardingStrategyFactory.newInstance,创建:ShardingStrategy
// 这是典型的工厂模式
// ************************************************************************************
databaseShardingStrategy =
(null == tableRuleConfig.getDatabaseShardingStrategyConfig()) ?
null :
//
ShardingStrategyFactory.newInstance(tableRuleConfig.getDatabaseShardingStrategyConfig());
tableShardingStrategy = null == tableRuleConfig.getTableShardingStrategyConfig() ? null : ShardingStrategyFactory.newInstance(tableRuleConfig.getTableShardingStrategyConfig());
final KeyGeneratorConfiguration keyGeneratorConfiguration = tableRuleConfig.getKeyGeneratorConfig();
generateKeyColumn = null != keyGeneratorConfiguration && !Strings.isNullOrEmpty(keyGeneratorConfiguration.getColumn()) ? keyGeneratorConfiguration.getColumn() : defaultGenerateKeyColumn;
// ************************************************************************************
// 10. 通过:ShardingKeyGeneratorServiceLoader创建:ShardingKeyGenerator
// 通过SPI来加载
// ************************************************************************************
shardingKeyGenerator = containsKeyGeneratorConfiguration(tableRuleConfig)
? new ShardingKeyGeneratorServiceLoader().newService(tableRuleConfig.getKeyGeneratorConfig().getType(), tableRuleConfig.getKeyGeneratorConfig().getProperties()) : null;
checkRule(dataNodes);
}
(6). ShardingStrategyFactory.newInstance
Shardind-JDBC对分库(分表)策略进行了归类,从现在的代码上来看,它只支持这几类的策略,也就是说:你只能基于这几种进行业务的扩展.
public static ShardingStrategy newInstance(final ShardingStrategyConfiguration shardingStrategyConfig) {
if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
// 精确分片算法
return new StandardShardingStrategy((StandardShardingStrategyConfiguration) shardingStrategyConfig);
}
if (shardingStrategyConfig instanceof InlineShardingStrategyConfiguration) {
// Inline分片算法
return new InlineShardingStrategy((InlineShardingStrategyConfiguration) shardingStrategyConfig);
}
if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
// 复合分片算法
return new ComplexShardingStrategy((ComplexShardingStrategyConfiguration) shardingStrategyConfig);
}
if (shardingStrategyConfig instanceof HintShardingStrategyConfiguration) {
// Hint分片算法
return new HintShardingStrategy((HintShardingStrategyConfiguration) shardingStrategyConfig);
}
// 不进行分片
return new NoneShardingStrategy();
}
(7). ShardingStrategy分片策略详解
<color=’red’>StandardShardingStrategy</color>:
StandardShardingStrategy标准分片策略,提供对SQL语句中的=,IN和BETWEEN AND的分片操作支持.
StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法.
PreciseShardingAlgorithm是必选的,用于处理=和IN的分片.
RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理.
<color=’red’>ComplexShardingStrategy</color>:
复合分片策略,提供对SQL语句中的=,IN和BETWEEN AND的分片操作支持.
ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此Sharding-JDBC并未做过多的封装,而是直接将分片键值组合以及分片操作符交于算法接口,完全由应用开发者实现,提供最大的灵活度.
<color=’red’>InlineShardingStrategy</color>:
Inline表达式分片策略,使用Groovy的Inline表达式,提供对SQL语句中的=和IN的分片操作支持. InlineShardingStrategy只支持单分片键,对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如:t_user${user_id % 8},表示t_user表按照user_id按8取模分成8个表,表名称为t_user_0到t_user_7.
<color=’red’>HintShardingStrategy</color>:
通过Hint而非SQL解析的方式分片的策略
<color=’red’>NoneShardingStrategy</color>:
不分片的策略
(8). ShardingAlgorithm类结构图
(9). 如何自定义分片策略呢
从代码我们分的出,newInstance是不提供扩展点给我们用的.但是.相应的ShardingStrategy的实现类,是可以支持我们对算法(ShardingAlgorithm)自由配置的.
- 自定义一个ShardingAlgorithm(例如:CustomerComplexKeysShardingAlgorithm),需要实现:ShardingAlgorithm的子接口.
- 将ShardingStrategy与ShardingAlgorithm过行绑定(注意:InlineShardingStrategy不支持该功能).
- 下面的代码以:YamlComplexShardingStrategyConfiguration为例,进行分析.
// **************************************YamlComplexShardingStrategyConfiguration**************************************
// 1. 对应的配置信息如下:
// spring.shardingsphere.sharding.tables.t_order.database-strategy.complex.sharding-columns=user_id,order_id
// spring.shardingsphere.sharding.tables.t_order.database-strategy.complex.algorithm-class-name=help.lixin.CustomerComplexKeysShardingAlgorithm
public final class YamlComplexShardingStrategyConfiguration
implements YamlBaseShardingStrategyConfiguration {
// 分片columns,多个之间用逗号隔开
private String shardingColumns;
// 分片的算法名称(需要全名称)
private String algorithmClassName;
} // end YamlComplexShardingStrategyConfiguration
// **************************************// ShardingStrategyConfigurationYamlSwapper**************************************
// 2. help.lixin.CustomerComplexKeysShardingAlgorithm加载时机.
public ShardingStrategyConfiguration swap(final YamlShardingStrategyConfiguration yamlConfiguration) {
// ... ...
if (null != yamlConfiguration.getComplex()) {
shardingStrategyConfigCount++;
result = new ComplexShardingStrategyConfiguration(
yamlConfiguration.getComplex().getShardingColumns(),
// *****************************************************************
// Class.forName(shardingAlgorithmClassName);
// 通过反射加载自定义的分片算法来着的.
// *****************************************************************
ShardingAlgorithmFactory.newInstance(yamlConfiguration.getComplex().getAlgorithmClassName(),ComplexKeysShardingAlgorithm.class));
}
// ... ...
Preconditions.checkArgument(shardingStrategyConfigCount <= 1, "Only allowed 0 or 1 sharding strategy configuration.");
return result;
}
(10). ShardingKeyGeneratorServiceLoader
public final class ShardingKeyGeneratorServiceLoader
extends TypeBasedSPIServiceLoader<ShardingKeyGenerator> {
static {
// 1. 通过SPI,加载:ShardingKeyGenerator的所有实现
NewInstanceServiceLoader.register(ShardingKeyGenerator.class);
}
public ShardingKeyGeneratorServiceLoader() {
// 2. 定义要加载的类型为:ShardingKeyGenerator
super(ShardingKeyGenerator.class);
}
} //end ShardingKeyGeneratorServiceLoader
(11). ShardingKeyGeneratorServiceLoader.newService
通过对:ShardingKeyGeneratorServiceLoader.newService进行分析.Sharding-JDBC提供了扩展点,允许自定义:ShardingKeyGenerator.
自定义ShardingKeyGenerator步骤如下:
- 自定义ShardingKeyGenerator(例如:CustomerShardingKeyGenerator).需要实现:ShardingKeyGenerator接口.
- 例如:CustomerShardingKeyGenerator.getType() == SNOWFLAKE_NEW.
- 配置SPI信息:/META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator(help.lixin.CustomerShardingKeyGenerator).
- 定义配置(spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE_NEW).
public abstract class TypeBasedSPIServiceLoader<T extends TypeBasedSPI> {
// ShardingKeyGenerator
private final Class<T> classType;
public final T newService(final String type, final Properties props) {
// type = "SNOWFLAKE"
// 1. loadTypeBasedServices
Collection<T> typeBasedServices = loadTypeBasedServices(type);
if (typeBasedServices.isEmpty()) {
throw new RuntimeException(String.format("Invalid `%s` SPI type `%s`.", classType.getName(), type));
}
T result = typeBasedServices.iterator().next();
result.setProperties(props);
return result;
} // end newService
private Collection<T> loadTypeBasedServices(final String type) {
// 4. 加载:ShardingKeyGenerator的所有实现,并获取实例中:getType() == SNOWFLAKE的ShardingKeyGenerator对象
return Collections2.filter(
// 2. 加载:ShardingKeyGenerator的所有实现类,并进行实例化.
NewInstanceServiceLoader.newServiceInstances(classType),
// 3. 所有:ShardingKeyGenerator的实现类,都要实现:getType()方法
input -> type.equalsIgnoreCase(input.getType()));
} // end loadTypeBasedServices
}
(12). 总结
这一部份,对TableRule进行了深入的分析,因为:它承载的信息太多,最主要是因为:后续可能会有一些业务需要自定义分片策略或主键生成策略.