《ShardingSphere解读》15 路由引擎:如何在路由过程中集成多种路由策略和路由算法?
上一篇中我们在介绍 ShardingRule 对象时引出了 ShardingSphere 路由引擎中的分片策略 ShardingStrategy分片策略是路由引擎中的一个核心概念直接影响了最终的路由结果。今天我们将围绕这一核心概念展开讨论。分片策略整体结构我们先来看分片策略 ShardingStrategy 的定义ShardingStrategy 位于 sharding-core-common 工程的 org.apache.shardingsphere.core.strategy.route 包中其定义如下所示public interface ShardingStrategy { //获取分片 Column Collection getShardingColumns(); //执行分片 Collection doSharding(Collection availableTargetNames, Collection shardingValues); }可以看到 ShardingStrategy 包含两个核心方法一个用于指定分片的 Column而另一个负责执行分片并返回目标 DataSource 和 Table。ShardingSphere 中为我们提供了一系列的分片策略实例类层结构如下所示ShardingStrategy 实现类图如果我们翻阅这些具体 ShardingStrategy 实现类的代码会发现每个 ShardingStrategy 中都会包含另一个与路由相关的核心概念即分片算法 ShardingAlgorithm我们发现 ShardingAlgorithm 是一个空接口但包含了四个继承接口即PreciseShardingAlgorithmRangeShardingAlgorithmComplexKeysShardingAlgorithmHintShardingAlgorithm而这四个接口又分别具有一批实现类ShardingAlgorithm 的类层结构如下所示ShardingAlgorithm 子接口和实现类图请注意ShardingStrategy 与 ShardingAlgorithm 之间并不是一对一的关系。在一个 ShardingStrategy 中可以同时使用多个 ShardingAlgorithm 来完成具体的路由执行策略。因此我们具有如下所示的类层结构关系图由于分片算法的独立性ShardingSphere 将其进行单独抽离。从关系上讲分片策略中包含了分片算法和分片键我们可以把分片策略的组成结构简单抽象成如下所示的公式分片策略 分片算法 分片键ShardingSphere 分片策略详解在 ShardingSphere 中一共存在五种 ShardingStrategy 实现不分片策略NoneShardingStrategyHint 分片策略HintShardingStrategy标准分片策略StandardShardingStrategy复合分片策略ComplexShardingStrategy行表达式分片策略InlineShardingStrategy接下来我们就对这些 ShardingStrategy一 一进行展开讨论。1.不分片策略 NoneShardingStrategy这次我们从简单的开始先来看 NoneShardingStrategy这是一种不执行分片的策略实现方式如下所示public final class NoneShardingStrategy implements ShardingStrategy { private final Collection shardingColumns Collections.emptyList(); Override public Collection doSharding(final Collection availableTargetNames, final Collection shardingValues) { return availableTargetNames; } }可以看到在 NoneShardingStrategy 中直接返回了输入的 availableTargetNames 而不执行任何具体路由操作。2.Hint 分片策略 HintShardingStrategy接下来我们来看 HintShardingStrategy回想我们在上一篇中通过这个 ShardingStrategy 来判断是否根据 Hint 进行路由。我们知道在有些场景下分片字段不是由 SQL 本身决定而由依赖于其他外置条件这时候就可使用 SQL Hint 灵活地注入分片字段。关于 Hint 的概念和前置路由的应用方式可以回顾 [《07 | 数据分片如何实现分库、分表、分库分表以及强制路由下》]中的内容。基于 HintShardingStrategy我们可以通过 Hint 而非 SQL 解析的方式执行分片策略。而 HintShardingStrategy 的实现依赖于HintShardingAlgorithmHintShardingAlgorithm 继承了 ShardingAlgorithm 接口。其定义如下所示可以看到该接口同样存在一个 doSharding 方法public interface HintShardingAlgorithm extends ShardingAlgorithm { //根据 Hint 信息执行分片 Collection doSharding(Collection availableTargetNames, HintShardingValue shardingValue); }对于 Hint 而言因为它实际上是对 SQL 执行过程的一种直接干预所以往往根据传入的 availableTargetNames 进行直接路由所以我们来看 ShardingSphere 中 HintShardingAlgorithm 接口唯一的一个实现类 DefaultHintShardingAlgorithmpublic final class DefaultHintShardingAlgorithm implements HintShardingAlgorithm { Override public Collection doSharding(final Collection availableTargetNames, final HintShardingValue shardingValue) { return availableTargetNames; } }可以看到这个分片算法的执行方式确实是基于 availableTargetNames但只是直接返回而已。所以对于 HintShardingStrategy 而言默认情况下实际上并没有执行任何路由效果。HintShardingStrategy 的完整实现如下所示public final class HintShardingStrategy implements ShardingStrategy { Getter private final Collection 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); //从配置中获取 HintShardingAlgorithm shardingAlgorithm hintShardingStrategyConfig.getShardingAlgorithm(); } SuppressWarnings(unchecked) Override public Collection doSharding(final Collection availableTargetNames, final Collection shardingValues) { ListRouteValue shardingValue (ListRouteValue) shardingValues.iterator().next(); Collection shardingResult shardingAlgorithm.doSharding(availableTargetNames, new HintShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), shardingValue.getValues())); Collection result new TreeSet(String.CASE_INSENSITIVE_ORDER); result.addAll(shardingResult); return result; } }我们注意到在 HintShardingStrategy 中shardingAlgorithm 变量的构建是通过 HintShardingStrategyConfiguration 配置类完成的显然我们可以通过配置项来设置具体的 HintShardingAlgorithm。在日常开发过程中我们一般都需要实现自定义的 HintShardingAlgorithm 并进行配置。[《07 | 数据分片如何实现分库、分表、分库分表以及强制路由下》]中演示了这种做法你可以做一些回顾。3.标准分片策略 StandardShardingStrategyStandardShardingStrategy 是一种标准分片策略提供对 SQL 语句中的, , , , , IN 和 BETWEEN AND 等操作的分片支持。我们知道分片策略相当于分片算法与分片键的组合。对于 StandardShardingStrategy 而言它只支持单分片键并提供PreciseShardingAlgorithm和RangeShardingAlgorithm这两个分片算法。PreciseShardingAlgorithm 是必选的用于处理 和 IN 的分片RangeShardingAlgorithm 是可选的用于处理 BETWEEN AND, , , , 分片。介绍 StandardShardingStrategy 之前我们先对其涉及的这两种分片算法分别进行讨论。1PreciseShardingAlgorithm对于 PreciseShardingAlgorithm 而言该接口用于处理使用单一键作为分片键的 和 IN 进行分片的场景。它有两个实现类分别是 PreciseModuloDatabaseShardingAlgorithm 和 PreciseModuloTableShardingAlgorithm。显然前者用于数据库级别的分片而后者面向表操作。它们的分片方法都一样就是使用取模Modulo操作。以 PreciseModuloDatabaseShardingAlgorithm 为例其实现如下所示public final class PreciseModuloDatabaseShardingAlgorithm implements PreciseShardingAlgorithm { Override public String doSharding(final Collection availableTargetNames, final PreciseShardingValue shardingValue) { for (String each : availableTargetNames) { //根据分片值执行对2的取模操作 if (each.endsWith(shardingValue.getValue() % 2 )) { return each; } } throw new UnsupportedOperationException(); } }可以看到这里对 PreciseShardingValue 进行了对 2 的取模计算并与传入的 availableTargetNames 进行比对从而决定目标数据库。2RangeShardingAlgorithm而对于 RangeShardingAlgorithm 而言情况就相对复杂。RangeShardingAlgorithm 同样具有两个实现类分别为 RangeModuloDatabaseShardingAlgorithm 和 RangeModuloTableShardingAlgorithm它们的命名和代码风格与 PreciseShardingAlgorithm 的实现类非常类似。这里也以 RangeModuloDatabaseShardingAlgorithm 为例它的实现如下所示public final class RangeModuloDatabaseShardingAlgorithm implements RangeShardingAlgorithm { Override public Collection doSharding(final Collection availableTargetNames, final RangeShardingValue shardingValue) { Collection result new LinkedHashSet(availableTargetNames.size()); //根据分片值决定分片的范围 for (Integer i shardingValue.getValueRange().lowerEndpoint(); i doSharding(final Collection availableTargetNames, final Collection shardingValues) { RouteValue shardingValue shardingValues.iterator().next(); Collection shardingResult shardingValue instanceof ListRouteValue //如果分片值是一个列表则执行 PreciseShardingAlgorithm ? doSharding(availableTargetNames, (ListRouteValue) shardingValue) //如果分片值是一个范围则 执行RangeShardingAlgorithm : doSharding(availableTargetNames, (RangeRouteValue) shardingValue); Collection result new TreeSet(String.CASE_INSENSITIVE_ORDER); result.addAll(shardingResult); return result; }可以看到这里根据传入的 shardingValues 的类型分别执行不同的 doSharding 方法如果输入的是ListRouteValue则会使用 PreciseShardingAlgorithm如下所示private Collection doSharding(final Collection availableTargetNames, final ListRouteValue shardingValue) { Collection result new LinkedList(); for (Comparable each : shardingValue.getValues()) { //使用 PreciseShardingAlgorithm 进行分片 String target preciseShardingAlgorithm.doSharding(availableTargetNames, new PreciseShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), each)); if (null ! target) { result.add(target); } } return result; }而如果是RangeRouteValue则使用 RangeShardingAlgorithm如下所示private Collection doSharding(final Collection availableTargetNames, final RangeRouteValue shardingValue) { if (null rangeShardingAlgorithm) { throw new UnsupportedOperationException(Cannot find range sharding strategy in sharding rule.); } //使用 RangeShardingAlgorithm 进行分片 return rangeShardingAlgorithm.doSharding(availableTargetNames, new RangeShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), shardingValue.getValueRange())); }4.复合分片策略 ComplexShardingStrategy与 StandardShardingStrategy 只支持单分片键不同ShardingSphere 的官网表明 ComplexShardingStrategy支持多分片键。ComplexShardingStrategy 的 doSharding 方法如下所示public Collection doSharding(final Collection availableTargetNames, final Collection shardingValues) { Map columnShardingValues new HashMap(shardingValues.size(), 1); Map columnRangeValues new HashMap(shardingValues.size(), 1); String logicTableName ; for (RouteValue each : shardingValues) { if (each instanceof ListRouteValue) { //构建 ListRouteValue columnShardingValues.put(each.getColumnName(), ((ListRouteValue) each).getValues()); } else if (each instanceof RangeRouteValue) { //构建 RangeRouteValue columnRangeValues.put(each.getColumnName(), ((RangeRouteValue) each).getValueRange()); } logicTableName each.getTableName(); } Collection shardingResult shardingAlgorithm.doSharding(availableTargetNames, new ComplexKeysShardingValue(logicTableName, columnShardingValues, columnRangeValues)); Collection result new TreeSet(String.CASE_INSENSITIVE_ORDER); result.addAll(shardingResult); return result; }这里基于传入的 RouteValue 分别构建了 ListRouteValue 和 RangeRouteValue然后传递给ComplexKeysShardingAlgorithm进行计算。由于多分片键之间的关系复杂因此 ComplexShardingStrategy 并未进行过多的封装而是直接将分片键值组合以及分片操作符透传至分片算法完全由应用开发者实现提供最大的灵活度。基于这一点考虑ShardingSphere 的 ComplexKeysShardingAlgorithm 的唯一实现类 DefaultComplexKeysShardingAlgorithm显得非常简单其代码如下所示public final class DefaultComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm { Override public Collection doSharding(final Collection availableTargetNames, final ComplexKeysShardingValue shardingValue) { return availableTargetNames; } }可以看到 DefaultComplexKeysShardingAlgorithm 与 NoneShardingStrategy 的实现实际上是一样的相当于就是什么都没有做也就是所有的工作都需要交给开发者自行进行设计和实现。5.行表达式分片策略 InlineShardingStrategy与前面介绍的各种分片策略相比InlineShardingStrategy 采用了一种特殊的机制来实现路由。我们已经在介绍分库分表案例中大量使用了行表达式也知道在使用行表达式时需要指定一个分片列 shardingColumn 以及一个类似 ds$-{user_id % 2} 的表达式。你可能会好奇 ShardingSphere 是如何来解析这样的表达式的呢基于 InlineShardingStrategy 定义的变量我们可以找到问题的答案//分片列 private final String shardingColumn; //Groovy 中的 Closure 实例 private final Closure closure;原来ShardingSphere 在这里用到了 Groovy 中的 Closure 对象。Groovy 是可运行在 JVM 中的一种动态语言既可以用于面向对象编程又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码同时又具有 Closure 和动态语言中的其他特性。在使用方式上基本也与使用 Java 代码的方式相同。基于 Groovy 的动态语言特性InlineShardingStrategy 提供对 SQL 语句中的 和 IN 的分片操作支持目前只支持单分片键。对于类似 ds$-{user_id % 2} 这样的常见分片算法可以通过简单配置进行使用从而避免烦琐的 Java 代码开发。我们直接来到 InlineShardingStrategy 的 doSharding 方法该方法的实现过程与标准分片策略 StandardShardingStrategy 中的相同不同的是需要通过 Groovy 进行解析输入参数从而获取最终路由结果private Collection doSharding(final ListRouteValue shardingValue) { Collection result new LinkedList(); for (PreciseShardingValue each : transferToPreciseShardingValues(shardingValue)) { //通过 execute 方法解析出最终的结果 result.add(execute(each)); } return result; }这里的 execute 方法中构建了 Groovy 的 Closure 对象并设置了对应的解析策略以及所需要解析的属性并最终返回解析的结果private String execute(final PreciseShardingValue shardingValue) { //构建 Groovy 的 Closur e对象 Closure result closure.rehydrate(new Expando(), null, null); result.setResolveStrategy(Closure.DELEGATE_ONLY); result.setProperty(shardingColumn, shardingValue.getValue()); //获取解析结果 return result.call().toString(); }最后作为总结我们要注意所有的 ShardingStrategy 相关类都位于 sharding-core-common 工程的 org.apache.shardingsphere.core.strategy 包下ShardingStrategy 相关类的包结构而所有的 ShardingAlgorithm 相关类则位于 sharding-core-api 工程的 org.apache.shardingsphere.api.sharding 包下ShardingAlgorithm 相关类的包结构我们在前面已经提到过 ShardingStrategy 的创建依赖于 ShardingStrategyConfigurationShardingSphere 也提供了一个 ShardingStrategyFactory 工厂类用于创建各种具体的 ShardingStrategypublic final class ShardingStrategyFactory { public static ShardingStrategy newInstance(final ShardingStrategyConfiguration shardingStrategyConfig) { if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) { return new StandardShardingStrategy((StandardShardingStrategyConfiguration) shardingStrategyConfig); } if (shardingStrategyConfig instanceof InlineShardingStrategyConfiguration) { return new InlineShardingStrategy((InlineShardingStrategyConfiguration) shardingStrategyConfig); } if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) { return new ComplexShardingStrategy((ComplexShardingStrategyConfiguration) shardingStrategyConfig); } if (shardingStrategyConfig instanceof HintShardingStrategyConfiguration) { return new HintShardingStrategy((HintShardingStrategyConfiguration) shardingStrategyConfig); } return new NoneShardingStrategy(); } }而这里用到的各种 ShardingStrategyConfiguration 也都位于 sharding-core-api 工程的org.apache.shardingsphere.api.sharding.strategy 包下ShardingStrategyConfiguration 相关类的包结构这样通过对路由引擎的介绍我们又接触到了一大批 ShardingSphere 中的源代码。至此关于 ShardingSphere 路由引擎部分的内容基本都介绍完毕。作为总结我们在《路由引擎如何理解分片路由核心类 ShardingRouter 的运作机制》中所给出的时序图中添加了 ShardingStrategy 和 ShardingAlgorithm 部分的内容如下所示从源码解析到日常开发在我们设计软件系统的过程中面对复杂业务场景时职责分离始终是需要考虑的一个设计点。ShardingSphere 对于分片策略的设计和实现很好地印证了这一观点。分片策略在 ShardingSphere 中实际上是一个比较复杂的概念但通过将分片的具体算法分离出去并提炼 ShardingAlgorithm 接口并构建 ShardingStrategy 和 ShardingAlgorithm 之间一对多的灵活关联关系我们可以更好地把握整个分片策略体系的类层结构这种职责分离机制同样可以应用与日常开发过程中。小结与预告承接上一篇的内容今天我们全面介绍了 ShardingSphere 中的五大分片策略和四种分片算法以及它们之间的组合关系。ShardingSphere 路由引擎中执行路由的过程正是依赖于这些分片策略和分片算法的功能特性。当然作为一款具有高扩展性的开源框架我们也可以基于自身的业务需求实现特定的分片算法并嵌入到具体的分片策略中。这里给你留一道思考题ShardingSphere 中分片策略与分片算法之间是如何协作的 欢迎你在留言区与大家讨论我将一一点评解答。在路由引擎的基础上下一篇将进入 ShardingSphere 分片引擎的另一个核心阶段即改写引擎。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2438010.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!