1.ShardingSphere数据分片的概念
1.1 主要通过分库和分表进行数据的拆分来使得各个表的数据量保持在阈值以下,以及对流量进行疏导应对高访问量,是应对高并发和海量数据系统的有效手段。 数据分片的拆分方式又分为垂直分片和水平分片(详情见官网:数据分片 :: ShardingSphere)
垂直分片:按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。 在拆分之前,一个数据库由多个数据表构成,每个表对应着不同的业务。而拆分之后,则是按照业务将表进行归类,分布到不同的数据库中,从而将压力分散至不同的数据库。 下图展示了根据业务需要,将用户表和订单表垂直分片到不同的数据库的方案
垂直分片往往需要对架构和设计进行调整。通常来讲,是来不及应对互联网业务需求快速变化的;而且,它也并无法真正的解决单点瓶颈。 垂直拆分可以缓解数据量和访问量带来的问题,但无法根治。如果垂直拆分之后,表中的数据量依然超过单节点所能承载的阈值,则需要水平分片来进一步处理
水平分片:水平分片又称为横向拆分。 相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。 例如:根据主键分片,偶数主键的记录放入0库(或表),奇数主键的记录放入1库(或表),如下图所示。
用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。 SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片
2.分片算法
通过分片算法将数据分片,支持通过=
、>=
、<=
、>
、<
、BETWEEN
和IN
分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高 目前提供4种分片算法。由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法
- 精确分片算法(PreciseShardingAlgorithm)
用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用
package com.shardingsphere.algorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; import java.util.Collection; public class DefaultModuloPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> { @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { String modulo = String.valueOf(shardingValue.getValue() % availableTargetNames.size()); for (String targetName : availableTargetNames) { if (targetName.endsWith(modulo)) return targetName; } throw new UnsupportedOperationException(); } }
如以上精准分片算法PreciseShardingAlgorithm 通过自定义的字段 进行奇偶数进行分库分表 (注意:进行分片的字段不能进行修改 如根据age进行分库 去修改age字段的话 就会报以下错误)
- 范围分片算法(RangeShardingAlgorithm)
- 用于处理使用单一键作为分片键的BETWEEN AND、>、<、>=、<=进行分片的场景。需要配合StandardShardingStrategy使用
-
package com.shardingsphere.algorithm; import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue; import java.util.Collection; import java.util.LinkedHashSet; import java.util.stream.Collectors; public class DefaultModuloRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) { Long lower = shardingValue.getValueRange().lowerEndpoint(); Long upper = shardingValue.getValueRange().upperEndpoint(); Collection<String> result = new LinkedHashSet<>(); for (Long i = lower; i <= upper; i++) { if (result.size() == availableTargetNames.size()) break; String modulo = String.valueOf(i % availableTargetNames.size()); result.addAll(availableTargetNames.stream().filter(m -> m.endsWith(modulo)).collect(Collectors.toList())); } return result; } }
- 复合分片算法(ComplexKeysShardingAlgorithm)
- 用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用
-
package com.shardingsphere.algorithm; import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm; import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue; import java.util.Collection; public class DefaultComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Long> shardingValue) { // 多片键的精确值场景 shardingValue.getColumnNameAndShardingValuesMap(); // 多片键的范围值场景 shardingValue.getColumnNameAndRangeValuesMap(); // 根据以上两种情况来筛选数据节点: 数据源、物理表... return availableTargetNames; } }
- Hint分片算法(HintShardingAlgorithm)
- 用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用
-
package com.shardingsphere.algorithm; import org.apache.shardingsphere.api.hint.HintManager; import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm; import org.apache.shardingsphere.api.sharding.hint.HintShardingValue; import java.util.Collection; import java.util.LinkedHashSet; /** * @see HintManager 指定的外部路由值来筛选目标库、表 */ public class DefaultHintShardingAlgorithm implements HintShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Long> shardingValue) { Collection<String> result = new LinkedHashSet<>(); for (String each : availableTargetNames) { for (Long value : shardingValue.getValues()) { if (each.endsWith(String.valueOf(value))) { result.add(each); } } } return result; } } 3.分片策略
- 包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。目前提供5种分片策略
- 标准分片策略
- 对应StandardShardingStrategy。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND, >, <, >=, <=分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理
- 复合分片策略
- 对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度
- 行表达式分片策略
- 对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如:
t_user_$->{u_id % 8}
表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0
到t_user_7
- Hint分片策略
- 对应HintShardingStrategy。通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略
- 不分片策略
- 对应NoneShardingStrategy。不分片的策略