ShardingSphere的三种分片策略 详解

1、ComplexShardingStrategy ?
2、HintShardingAlgorithm ?
3、StandardShardingAlgorithm?

下面我来详细拆解这三种分片策略,帮你更好地理解它们的区别。

总的来说,这三种策略分别解决了不同层面的路由问题:Standard 应对单维精确与范围查询,Complex 负责多维联合路由,Hint 则实现了代码层面的强制干预。你可以先通过这个速查表快速了解它们的核心区别:

特性维度🎯 标准分片策略 (Standard)🧩 复合分片策略 (Complex)✨ 强制路由分片策略 (Hint)
分片键数量单一分片键-28多个分片键-28无分片键,或忽略SQL中的分片键-12
操作符支持=INBETWEEN AND><>=<=-28=INBETWEEN AND><>=<=-28通过代码指定路由目标,不受SQL操作符限制-12
核心实现需实现 PreciseShardingAlgorithm (必选) 和 RangeShardingAlgorithm (可选)-28需实现 ComplexKeysShardingAlgorithm 接口,处理复合逻辑-28需实现 HintShardingAlgorithm 接口,通过 HintManager 注入分片值-28
灵活性极高
实现复杂度低 (API使用简单)
核心优势能高效处理单键的范围查询,实现更精确的路由可根据多字段组合,实现强大的自定义路由可强制指定路由目标,突破SQL限制,实现灵活干预
主要局限无法处理多字段联合分片-21需自行处理多分片键的复杂逻辑,有全表扫描风险-API侵入性强,过度使用会破坏架构的清晰度-21

1. 🎯 标准分片策略 (Standard)

概念、作用与功能

正如其名,这是最标准、最常用的策略,是分库分表的“基本款”。针对的是只有一个分片键,但查询条件多样的场景。它不仅能处理 = 和 IN 这样的精确查找,还能智能处理 BETWEEN AND 和 > < 这类范围查询。

与复杂逻辑都塞在一个类里的复合策略不同,Standard 策略通过 PreciseShardingAlgorithm (处理精确查询)-36 和 RangeShardingAlgorithm (处理范围查询)-36 两个更细粒度的算法接口,实现了关注点分离,让代码逻辑更清晰。

优缺点分析
  • 👍 优点:支持复杂SQL操作符,处理范围查询和精确查询的性能都很高-21

  • 👎 缺点:只能基于一个字段分片。如果你的查询条件里经常同时出现 user_id 和 order_id,用这个策略就爱莫能助了-21

实战演示

下面是一个完整的代码示例,展示如何实现一个自定义的标准分片算法,根据 order_id 的奇偶性将其路由到不同的数据库表。

1. 实现自定义分片算法
创建一个Java类,实现StandardShardingAlgorithm接口-36。ShardingSphere会自动根据SQL操作符,调用对应的doSharding方法。

public class MyStandardShardingAlgorithm implements StandardShardingAlgorithm<Long> {

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        // 处理 SQL 中的 = 和 IN 操作符
        Long orderId = shardingValue.getValue();
        String suffix = String.valueOf(orderId % 2);
        for (String targetName : availableTargetNames) {
            if (targetName.endsWith(suffix)) {
                return targetName;
            }
        }
        return null;
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
        // 处理 SQL 中的 BETWEEN AND、>、< 等范围操作符
        Collection<String> result = new LinkedHashSet<>();
        Range<Long> valueRange = shardingValue.getValueRange();
        for (Long i = valueRange.lowerEndpoint(); i <= valueRange.upperEndpoint(); i++) {
            String suffix = String.valueOf(i % 2);
            for (String targetName : availableTargetNames) {
                if (targetName.endsWith(suffix)) {
                    result.add(targetName);
                }
            }
        }
        return result;
    }

    // 其他必要方法(init, getProps等)略...
}

2. 配置分片策略
在配置文件中指定strategy: STANDARD,并设置分片键order_id-36

yaml

# 分片算法定义
shardingAlgorithms:
  standard-algorithm:
    type: CLASS_BASED
    props:
      strategy: STANDARD
      algorithmClassName: com.example.MyStandardShardingAlgorithm

# 表策略配置
t_order:
  actualDataNodes: ds0.t_order_${0..1}
  tableStrategy:
    standard:
      shardingColumn: order_id
      shardingAlgorithmName: standard-algorithm

2. 🧩 复合分片策略 (Complex)

概念、作用与功能

当业务无法通过单一字段决定数据归属时,就需要复合分片策略。它支持使用两个及以上的字段作为联合分片键-28-1。因多分片键关系复杂,ShardingSphere不提供默认实现,而是将完整的分片键值组合直接交给开发者,给予最大的灵活度-。

优缺点分析
  • 👍 优点:支持多字段联合分片,能实现更贴近业务的、复杂的路由逻辑-21

  • 👎 缺点:需要自行处理多分片键组合逻辑,复杂度高;实现不当的SQL可能引发全库表路由,造成性能问题-。

实战演示

这个示例展示了如何根据 user_id 和 region_code(区域码)两个字段,共同决定数据最终路由到哪张表。

1. 实现复合分片算法
创建一个Java类,实现ComplexKeysShardingAlgorithm接口。

public class MyComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                          ComplexKeysShardingValue<Long> shardingValue) {
        // 获取所有分片键的列名和对应的值
        Map<String, Collection<Long>> columnValuesMap = shardingValue.getColumnNameAndShardingValuesMap();
        Collection<Long> userIds = columnValuesMap.get("user_id");
        Collection<Long> regionCodes = columnValuesMap.get("region_code");
        Set<String> result = new HashSet<>();

        for (Long userId : userIds) {
            for (Long regionCode : regionCodes) {
                // 自定义复杂分片逻辑,例如:按user_id取模,再根据region_code决定表后缀
                String suffix = ((userId % 4) + (regionCode % 2)) % 2 + "";
                for (String targetName : availableTargetNames) {
                    if (targetName.endsWith(suffix)) {
                        result.add(targetName);
                    }
                }
            }
        }
        return result;
    }

    // 其他必要方法略...
}

2. 配置分片策略
在配置文件中指定strategy: COMPLEX,并列出所有分片键shardingColumns: user_id, region_code-35

yaml

# 分片算法定义
shardingAlgorithms:
  complex-algorithm:
    type: CLASS_BASED
    props:
      strategy: COMPLEX
      algorithmClassName: com.example.MyComplexKeysShardingAlgorithm

# 表策略配置
t_user_log:
  actualDataNodes: ds0.t_user_log_${0..1}
  tableStrategy:
    complex:
      shardingColumns: user_id, region_code
      shardingAlgorithmName: complex-algorithm

注意:复合分片策略对开发者的要求较高,分片逻辑不当容易导致全库表扫描,严重影响性能。上线前务必做好充分测试-。


3. ✨ 强制路由分片策略 (Hint)

概念、作用与功能

这是为解决“分片键不在SQL中”这类棘手问题而生的“秘密武器”。当分片信息需要从外部(如用户会话、业务上下文)获取时,常规策略就失效了。Hint策略允许你在代码中通过HintManager手动指定路由目标-12,使SQL能无视原有逻辑,直接路由到指定节点-50

优缺点分析
  • 👍 优点:解决了无分片键字段的SQL路由难题,可实现强制主库路由(读写分离)、指定库表测试等精细操作-50

  • 👎 缺点:业务代码侵入性强,需要手动管理HintManager的生命周期-21。使用不当可能绕过精心设计的分片逻辑,造成数据分布混乱。

实战演示

下面是一个综合示例,展示了Hint的两种常见用法:一种是强制路由到指定数据节点;另一种是在读写分离场景下,强制查询走主库以保证数据实时性。

1. 实现Hint分片算法
创建一个Java类,实现HintShardingAlgorithm接口。

public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Long> {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                          HintShardingValue<Long> shardingValue) {
        Collection<String> result = new ArrayList<>();
        // 获取通过 HintManager 注入的分片值
        for (Long value : shardingValue.getValues()) {
            for (String targetName : availableTargetNames) {
                if (targetName.endsWith(String.valueOf(value % 2))) {
                    result.add(targetName);
                }
            }
        }
        return result;
    }

    // 其他必要方法略...
}

2. 配置分片策略
在配置文件中指定strategy: HINT,将其定义为Hint策略。

yaml

# 分片算法定义
shardingAlgorithms:
  hint-algorithm:
    type: CLASS_BASED
    props:
      strategy: HINT
      algorithmClassName: com.example.MyHintShardingAlgorithm

# 表策略配置
t_order:
  actualDataNodes: ds0.t_order_${0..1}
  tableStrategy:
    hint:
      shardingAlgorithmName: hint-algorithm

3. 在业务代码中使用 HintManager
在代码中通过HintManager来实际控制路由。

// 场景一:强制路由到指定库表
@Test
public void testHintInsert() {
    try (HintManager hintManager = HintManager.getInstance()) {
        // 指定路由到数据库后缀为1,表后缀为0的节点
        hintManager.addDatabaseShardingValue("t_order", 1L);
        hintManager.addTableShardingValue("t_order", 0L);
        // 执行数据插入操作
        orderMapper.insert(order);
    }
}

// 场景二:强制读写分离下的主库路由
@Test
public void testForceMaster() {
    try (HintManager hintManager = HintManager.getInstance()) {
        // 强制本次查询走主库,避免主从同步延迟
        hintManager.setMasterRouteOnly();
        List<Order> orders = orderMapper.selectRecentOrders();
    }
}

💎 总结与建议

归根结底,这三种策略服务于不同的分片场景。我建议你可以参考下面这个思路来选择:

  1. 优先选择标准策略:如果你的分片逻辑主要围绕一个业务字段展开,并且需要支持范围查询,StandardShardingAlgorithm 是最佳选择。

  2. 考虑复合策略:当业务规则强依赖多个字段(如 (user_id, order_id))才能确定数据位置时,再选用 ComplexShardingAlgorithm

  3. 保留 Hint 作为秘密武器:当分片键无法从SQL中获取,或在读写分离等特殊场景下需要“强制干预”路由时,Hint 是最佳解决方案,但切记不宜过度使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值