ComplexKeysShardingAlgorithm 是 Apache ShardingSphere 中的一种复合分片算法 。它的核心作用是让你能基于多个业务字段(比如user_id + order_id),完全自定义数据如何分布到不同的数据库或表中-。因为它的高度定制性,你需要自己实现具体的分片逻辑-1。
我把它的核心信息整理成了一个表格,方便你快速了解:
| 方面 | 详细说明 |
|---|---|
| 核心接口 | ComplexKeysShardingAlgorithm<T extends Comparable<?>>- |
| 配合策略 | ComplexShardingStrategy- |
| 关键方法 | Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<T> shardingValue)-13 |
| 主要目标 | 1. 基于多字段(如用户ID、区域、时间等)进行复合路由计算。 2. 解决单字段分片键无法满足的复杂业务分片需求。 |
| 典型应用 | 多租户SaaS系统(tenant_id + user_id)、金融订单系统(user_id + order_id)、需按时间+地理维度分片的数据平台。 |
🎯 概念、作用与使用场景分析
当业务增长,数据量巨大时,常会用到"分库分表"。ComplexKeysShardingAlgorithm就是为了解决复杂分片需求而生的:
-
提供最大灵活度 :ShardingSphere不对多分片键的组合逻辑做任何预设,而是将原始的分片键值、操作符(
=,>等)都交给开发者,让开发者实现最贴合业务的分片逻辑--16。 -
支持复杂SQL操作 :该算法能处理SQL中包含
=,>,<,>=,<=,IN和BETWEEN AND等多种操作符的场景,非常全面-1-3。 -
核心应用场景 :它非常适合多租户系统(分片键:
tenant_id,user_id)、订单系统(分片键:user_id,order_id)以及对查询性能要求极高的复杂业务场景-。
⚖️ 优缺点解析
-
优点:
-
缺点:
💻 实战演示:基于用户ID与省份的复合分片
下面通过一个自定义复合分片的示例,帮你更好地理解其实现机制。
1. 核心依赖
在项目中引入核心依赖,注意建议使用较新且稳定的版本。
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version> <!-- 请根据项目实际情况选择合适版本 -->
</dependency>
2. 完整算法实现
创建一个Java类,实现ComplexKeysShardingAlgorithm接口。
public class UserProvinceTableShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames,
ComplexKeysShardingValue<Long> shardingValue) {
// 1. 获取逻辑表名,例如:t_order
String logicTableName = shardingValue.getLogicTableName();
// 2. 获取分片键对应的值集合
Map<String, Collection<Long>> columnValuesMap = shardingValue.getColumnNameAndShardingValuesMap();
Collection<Long> userIds = columnValuesMap.get("user_id");
Collection<Long> provinceIds = columnValuesMap.get("province_id");
// 3. 待路由的表名集合
Set<String> actualTableNames = new HashSet<>();
// 4. 处理IN查询,可能包含多个user_id
if (userIds != null && !userIds.isEmpty()) {
for (Long userId : userIds) {
// 利用多个字段值来计算,例如:根据用户ID%4和省份ID%2组合计算表后缀
Long tableSuffix = (userId % 4) * 2 + (provinceIds != null ? provinceIds.iterator().next() % 2 : 0);
String actualTableName = logicTableName + "_" + tableSuffix;
if (availableTargetNames.contains(actualTableName)) {
actualTableNames.add(actualTableName);
}
}
} else if (provinceIds != null && !provinceIds.isEmpty()) {
// 处理只有省份ID的查询
for (Long provinceId : provinceIds) {
// ... 基于省份ID的路由逻辑,需要确保能覆盖所有可能的数据分布
}
}
// 5. 返回目标物理表集合
return actualTableNames;
}
}
3. 配置方式
在application.properties或application.yml中配置数据源、分片规则和自定义算法。
properties
# 数据源配置省略...
# 配置order_logic表的分片策略
spring.shardingsphere.sharding.tables.order_logic.actual-data-nodes=ds0.order_logic_$->{0..7}
# 配置分库策略(使用行表达式简单分库)
spring.shardingsphere.sharding.tables.order_logic.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.order_logic.database-strategy.inline.algorithm-expression=ds${user_id % 2}
# 配置分表策略(使用复合分片算法)
spring.shardingsphere.sharding.tables.order_logic.table-strategy.complex.sharding-columns=user_id,province_id
spring.shardingsphere.sharding.tables.order_logic.table-strategy.complex.algorithm-class-name=com.example.UserProvinceTableShardingAlgorithm
--
📌 实际应用场景示例
-
多租户订单系统 :以
tenant_id(租户ID)和order_id(订单ID)联合分片,强制租户隔离并分散热点--8。 -
电商用户行为表 :以
user_id和event_date联合分片,用user_id保证数据关联性,用event_date管理时间范围查询-2。 -
物联网设备数据 :以
product_line(产品线)和device_id(设备ID)联合分片,优先确保高优先级业务的数据隔离,再在内部均衡分布。
🔍 工作原理详解
-
入口与策略 :
ComplexShardingStrategy(复合分片策略)是入口,它将多分片键值和操作符完整地传递给ComplexKeysShardingAlgorithm.doSharding(...)方法进行最终决策-16-25-4。 -
SPI机制与版本差异 :ShardingSphere通过SPI机制加载算法实现。注意版本差异 :在4.x及更早版本,一个
ComplexKeysShardingAlgorithm实现需同时处理精确、IN和范围查询;而从5.x开始,官方更推荐使用StandardShardingAlgorithm+RangeShardingAlgorithm的模式来实现复合分片功能--1。
💡 最佳实践与注意事项
-
优先选择标准算法 :若单分片键能解决问题,优先用
StandardShardingAlgorithm,它更简单且性能更好-8-22。 -
合理设置分片键 :分片键应尽量包含在常用SQL的
WHERE条件中,避免路由到所有分片。索引设计要与分片策略协同,避免SQL解析性能瓶颈-8。 -
警惕版本兼容性 :升级ShardingSphere时需重审算法实现,确保
doSharding方法的参数和ComplexKeysShardingValue的结构匹配新版本。 -
处理跨库查询:尽量减少跨库关联查询,通过数据冗余或字段冗余规避。需权衡跨库性能与数据一致性。
💎 总结
简单总结,ComplexKeysShardingAlgorithm 为你提供了高度的灵活性,但同时也伴随着实现和运维上的复杂性。如果简单分片已够用,优先选择标准算法(如基于INLINE表达式);只有当业务必须使用多字段进行分片路由时,才应该使用复合分片算法。