ShardingSphere自定义分片策略案例
可以,把这三个接口理解成三种“分片决策方式”:
- Standard:只看一个分片键,SQL 里能直接看到。
- Complex:看多个分片键,需要你自己组合计算。
- Hint:SQL 里没有分片键,靠代码额外传进来。
下面每个都举一个通俗案例。
1. StandardShardingAlgorithm:订单按 order_id 分表
业务场景
订单表 t_order 数据量太大,准备分成 4 张表:
t_order_0t_order_1t_order_2t_order_3
规则很简单:按订单 ID 取模。
order_id % 4 = 0 进 t_order_0,1 进 t_order_1,以此类推。
SQL 示例
-- 精准查询:知道订单 ID
SELECT * FROM t_order WHERE order_id = 1001;
-- 1001 % 4 = 1,所以去 t_order_1
-- IN 查询
SELECT * FROM t_order WHERE order_id IN (1001, 1002, 1003);
-- 分别路由到 t_order_1、t_order_2、t_order_3
-- 范围查询
SELECT * FROM t_order WHERE order_id BETWEEN 1001 AND 1010;
-- 范围跨多张表,需要返回可能涉及的表
自定义算法代码
public class OrderStandardAlgorithm implements StandardShardingAlgorithm<Long> {
// 处理 =、IN
@Override
public String doSharding(Collection<String> tables,
PreciseShardingValue<Long> shardingValue) {
Long orderId = shardingValue.getValue();
String targetTable = "t_order_" + (orderId % 4);
if (tables.contains(targetTable)) {
return targetTable;
}
throw new UnsupportedOperationException("没有找到表: " + targetTable);
}
// 处理 BETWEEN、>、<
@Override
public Collection<String> doSharding(Collection<String> tables,
RangeShardingValue<Long> shardingValue) {
// 简单写法:范围查询返回所有表,避免漏数据
// 实际生产建议根据范围上下界计算真正需要的表
return tables;
}
}
配置示例
spring:
shardingsphere:
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds0.t_order_$->{0..3}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-standard
sharding-algorithms:
order-standard:
type: CLASS_BASED
props:
strategy: STANDARD
algorithmClassName: com.example.OrderStandardAlgorithm
通俗理解
就像快递分拣:
只看一个“订单号”,尾号是几就扔到几号柜子。
这就是 Standard:单分片键,SQL 里直接带 order_id。
2. ComplexKeysShardingAlgorithm:订单按 user_id + order_id 组合分表
业务场景
还是订单表,但业务希望:
- 同一个用户的订单尽量集中;
- 同时订单也要分散到多张表。
于是分片键有两个:
user_idorder_id
分片规则:(user_id + order_id) % 4,分到 t_order_0 ~ t_order_3。
SQL 示例
-- 两个分片键都有,可以精确算出表
SELECT * FROM t_order WHERE user_id = 123 AND order_id = 456;
-- (123 + 456) % 4 = 579 % 4 = 3,去 t_order_3
-- 只带 user_id,无法唯一确定,需要返回所有可能表
SELECT * FROM t_order WHERE user_id = 123;
-- 只带 order_id,也无法唯一确定
SELECT * FROM t_order WHERE order_id = 456;
自定义算法代码
public class OrderComplexAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> tables,
Collection<ShardingValue> shardingValues) {
Map<String, Long> valueMap = new HashMap<>();
for (ShardingValue sv : shardingValues) {
if (sv instanceof PreciseShardingValue) {
PreciseShardingValue<Long> precise = (PreciseShardingValue<Long>) sv;
valueMap.put(precise.getColumnName(), precise.getValue());
}
}
Long userId = valueMap.get("user_id");
Long orderId = valueMap.get("order_id");
// 两个键都有,精确路由
if (userId != null && orderId != null) {
String targetTable = "t_order_" + ((userId + orderId) % 4);
return Collections.singletonList(targetTable);
}
// 缺少任意一个键,无法精确路由,返回所有表
return tables;
}
}
配置示例
spring:
shardingsphere:
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds0.t_order_$->{0..3}
table-strategy:
complex:
sharding-columns: user_id, order_id
sharding-algorithm-name: order-complex
sharding-algorithms:
order-complex:
type: CLASS_BASED
props:
strategy: COMPLEX
algorithmClassName: com.example.OrderComplexAlgorithm
通俗理解
就像寄快递时,不仅看订单号,还要看用户所在城市。
两个条件一起决定包裹去哪个仓库。
这就是 Complex:多分片键,需要自己组合判断。
3. HintShardingAlgorithm:多租户按 tenant_id 分表,但 SQL 里不带 tenant_id
业务场景
SaaS 系统,每个租户一张订单表:
- 租户 101:
t_order_101 - 租户 102:
t_order_102 - 租户 103:
t_order_103
但是 SQL 里不写租户 ID,因为租户信息在登录后放在 ThreadLocal 或请求上下文里。
比如当前登录用户属于租户 101,代码里强制路由到 t_order_101。
SQL 示例
-- SQL 里没有 tenant_id
SELECT * FROM t_order WHERE order_id = 1001;
代码里设置 Hint
HintManager hintManager = HintManager.getInstance();
hintManager.setShardingValue("tenant_id", 101L);
// 执行 SQL,ShardingSphere 会从 Hint 中取 tenant_id
jdbcTemplate.query("SELECT * FROM t_order WHERE order_id = 1001", ...);
hintManager.close();
自定义算法代码
public class TenantHintAlgorithm implements HintShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> tables,
HintShardingValue<Long> hintValue) {
Long tenantId = hintValue.getValues().iterator().next();
String targetTable = "t_order_" + tenantId;
if (tables.contains(targetTable)) {
return Collections.singletonList(targetTable);
}
throw new UnsupportedOperationException("表不存在: " + targetTable);
}
}
配置示例
spring:
shardingsphere:
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds0.t_order_$->{101..103}
table-strategy:
hint:
sharding-algorithm-name: tenant-hint
sharding-algorithms:
tenant-hint:
type: CLASS_BASED
props:
strategy: HINT
algorithmClassName: com.example.TenantHintAlgorithm
通俗理解
就像快递单上没写“哪个小区”,但系统后台知道你现在属于哪个小区,强制把包裹分到那个小区。
这就是 Hint:分片键不在 SQL 中,由外部条件决定。
总结对比
| 接口 | 分片键数量 | 分片键位置 | 典型场景 | 通俗类比 |
|---|---|---|---|---|
| StandardShardingAlgorithm | 1 个 | SQL 里 | 订单按 order_id 分表 | 只看订单号尾号 |
| ComplexKeysShardingAlgorithm | 多个 | SQL 里 | 订单按 user_id + order_id 分表 | 同时看用户和订单号 |
| HintShardingAlgorithm | 0 个 | SQL 外 | 多租户、灰度、强制路由 | 系统后台偷偷指定区域 |
选择原则:
- 只有一个分片键,用 Standard;
- 有多个分片键,用 Complex;
- SQL 里根本看不到分片键,用 Hint。
浙公网安备 33010602011771号