前言
通过前文,我们可以知道,不管是简单路由还是复杂路由,最终都是通过SQL计算出最小执行单元,
也就计算该SQL涉及到哪些数据源,哪些表。 在计算这个的时候,都会用到通过ShardingStrategy
这个类来处理。今天主要就是讲这个类。
ShardingStrategy
这个类都会通过当前的分表规则获取。
代码片段如下:
分库
private Collection<String> routeDataSources(final TableRule tableRule) {
// 根据tableRule获取分库规则
DatabaseShardingStrategy strategy = shardingRule.getDatabaseShardingStrategy(tableRule);
// ... 省略代码
// 静态分片
Collection<String> result = strategy.doStaticSharding(sqlStatement.getType(), tableRule.getActualDatasourceNames(), shardingValues);
return result;
}
分库规则没有动态分片,仅有静态分片。
分表
private Collection<String> routeTables(final TableRule tableRule, final Collection<String> routedDataSources) {
// 获取分表规则
TableShardingStrategy strategy = shardingRule.getTableShardingStrategy(tableRule);
// 判断是静态分片,还是动态分片。
Collection<String> result = tableRule.isDynamic() ? strategy.doDynamicSharding(shardingValues)
: strategy.doStaticSharding(sqlStatement.getType(), tableRule.getActualTableNames(routedDataSources), shardingValues);
// 省略代码N行
return result;
}
静态分片
/**
* 计算静态分片.
*
* @param sqlType SQL语句的类型
* @param availableTargetNames 所有的可用分片资源集合
* @param shardingValues 分片值集合
* @return 分库后指向的数据源名称集合
*/
public Collection<String> doStaticSharding(final SQLType sqlType, final Collection<String> availableTargetNames, final Collection<ShardingValue<?>> shardingValues) {
Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
// 判断分片值是否为空
if (shardingValues.isEmpty()) {
// 否则,直接返回所有目标资源
result.addAll(availableTargetNames);
} else {
// 调用分片方法
result.addAll(doSharding(shardingValues, availableTargetNames));
}
return result;
}
PS : 本文源码是根据1.5.4的版本说的,之前的版本在分片值为空的时候,还需要做以下判断 , 在本版本里面,作者把这个校验代码摘除了
,这也导致了,如果做了分表,但是insert语句里面不包含分表的分片字段,**那么将会把路由的到的表里面全部插入该数据。 **
// 分片值为空,则判断SQLType是否为insert类型 ,如果是insert类型,则报异常
Preconditions.checkState(!isInsertMultiple(sqlType, availableTargetNames), "");
动态分片
/**
* 计算动态分片.
*
* @param shardingValues 分片值集合
* @return 分库后指向的分片资源集合
*/
public Collection<String> doDynamicSharding(final Collection<ShardingValue<?>> shardingValues) {
// 动态分片,分片值必须要有,否则报错。
Preconditions.checkState(!shardingValues.isEmpty(), "Dynamic table should contain sharding value.");
Collection<String> availableTargetNames = Collections.emptyList();
Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
// 调用分片算法
result.addAll(doSharding(shardingValues, availableTargetNames));
return result;
}
分片算法
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<String> doSharding(final Collection<ShardingValue<?>> shardingValues, final Collection<String> availableTargetNames) {
// 分片规则是NoneKeyShardingAlgorithm ,表示没有分片,直接返回。
if (shardingAlgorithm instanceof NoneKeyShardingAlgorithm) {
return Collections.singletonList(((
NoneKeyShardingAlgorithm) shardingAlgorithm).doSharding(
availableTargetNames, shardingValues.iterator().next()));
}
// 单键分片
if (shardingAlgorithm instanceof SingleKeyShardingAlgorithm) {
SingleKeyShardingAlgorithm<?> singleKeyShardingAlgorithm
= (SingleKeyShardingAlgorithm<?>) shardingAlgorithm;
ShardingValue shardingValue = shardingValues.iterator().next();
switch (shardingValue.getType()) {
case SINGLE: // ==条件
return Collections.singletonList(singleKeyShardingAlgorithm.doEqualSharding(
availableTargetNames, shardingValue));
case LIST: // in 查找
return singleKeyShardingAlgorithm.doInSharding(
availableTargetNames, shardingValue);
case RANGE: // Between 范围查找
return singleKeyShardingAlgorithm.doBetweenSharding(
availableTargetNames, shardingValue);
default:
throw new UnsupportedOperationException(
shardingValue.getType().getClass().getName());
}
}
// 多键分片
if (shardingAlgorithm instanceof MultipleKeysShardingAlgorithm) {
return ((MultipleKeysShardingAlgorithm) shardingAlgorithm).doSharding(
availableTargetNames, shardingValues);
}
throw new UnsupportedOperationException(shardingAlgorithm.getClass().getName());
}
在单键分片那个switch里面,通过分片的操作类型,分别调用我们自定义的分片策略的
doEqualSharding
doInSharding
doBetweenSharding
这三个方法,然后获取返回计算出的分片结果,
单键分片静态分片例子
/**
* 分表策略的基本实现
*/
public class ModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {
@Override
public String doEqualSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
for (String each : tableNames) {
if (each.endsWith(shardingValue.getValue() % 2 + "")) {
return each;
}
}
throw new IllegalArgumentException();
}
@Override
public Collection<String> doInSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
Collection<String> result = new LinkedHashSet<String>(tableNames.size());
for (Long value : shardingValue.getValues()) {
for (String tableName : tableNames) {
if (tableName.endsWith(value % 2 + "")) {
result.add(tableName);
}
}
}
return result;
}
@Override
public Collection<String> doBetweenSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
Collection<String> result = new LinkedHashSet<String>(tableNames.size());
Range<Long> range = (Range<Long>) shardingValue.getValueRange();
for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String each : tableNames) {
if (each.endsWith(i % 2 + "")) {
result.add(each);
}
}
}
return result;
}
}
单键分片动态分片例子
public final class SingleKeyDynamicModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Integer> {
private final String tablePrefix;
@Override
public String doEqualSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
return tablePrefix + shardingValue.getValue() % 10;
}
@Override
public Collection<String> doInSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
Collection<String> result = new LinkedHashSet<>(shardingValue.getValues().size());
for (Integer value : shardingValue.getValues()) {
result.add(tablePrefix + value % 10);
}
return result;
}
@Override
public Collection<String> doBetweenSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
Range<Integer> range = shardingValue.getValueRange();
for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
result.add(tablePrefix + i % 10);
}
return result;
}
}
静态分片和动态分片的区别在于,静态分片是已经定义好了真实表的,动态分片在于表名可以在分片算法中动态计算出来,动态分片,可以做按月动态分表。