ShardingSphere-JDBC 分片策略 详解

ShardingSphere-JDBC 的分片策略(Sharding Strategy) 是分库分表功能的核心,它定义了数据如何根据分片键(Sharding Key)被路由到不同的数据库或数据表的规则。一个完整的分片策略由两部分组成:分片键(Sharding Key)分片算法(Sharding Algorithm)

核心公式: 分片策略 = 分片键 + 分片算法


一、 分片键 (Sharding Key)

  • 定义: 用于进行分片计算的数据库表字段。它是决定数据最终落到哪个物理库、哪个物理表的关键依据。
  • 重要性:
    • 选择恰当的分片键对性能、扩展性和查询效率至关重要。
    • 应选择数据分布均匀业务查询频繁使用(如 WHERE 条件、JOIN 条件)的字段。
  • 常见分片键:
    • user_id (用户ID)
    • order_id (订单ID)
    • tenant_id (租户ID)
    • product_id (商品ID)
    • create_time (创建时间 - 常用于按时间范围分片)
  • 多分片键: ShardingSphere 支持使用多个字段作为复合分片键(如 user_id + order_type),需要配置ComplexShardingStrategy并使用ComplexKeysShardingAlgorithm

二、 分片算法 (Sharding Algorithm)

分片算法是执行具体路由计算的逻辑。ShardingSphere 提供了多种内置算法,也支持完全自定义。主要分为两大类:

  1. 标准分片算法 (StandardShardingAlgorithm)

    • 作用: 主要处理精确分片场景,即 SQL 条件中包含 =IN 操作符的分片键查询。
    • 核心接口:
      • doSharding(availableTargetNames, shardingValue):根据传入的分片值计算目标数据源或表。
        • availableTargetNames:所有可用的目标库名或表名集合(如 ["ds0", "ds1"]["t_order_0", "t_order_1"])。
        • shardingValue:包含分片键、逻辑表名和本次 SQL 中该分片键的实际值(或值列表)。
    • 常用内置实现:
      • INLINE: 基于 Groovy 表达式的轻量级算法。最常用,推荐优先考虑。
        • 配置示例 (algorithm-expression):
          • 库分片:ds${user_id % 2} (用户ID模2,分配到 ds0 或 ds1)
          • 表分片:t_order_${order_id % 16} (订单ID模16,分配到 t_order_0 到 t_order_15)
      • HASH_MOD: 哈希取模算法(先对分片值做哈希,再取模)。
      • MOD: 简单取模算法(直接对分片值取模)。
      • VOLUME_RANGE: 基于分片值范围的分片算法(需要配置 range-lowerrange-upper 定义每个分片承载的数据范围)。
      • BOUNDARY_RANGE: 基于分片值边界列表的分片算法(需配置 sharding-ranges 指定精确边界)。
      • CLASS_BASED: 指向一个自定义实现了 StandardShardingAlgorithm 接口的 Java 类。用于实现复杂或业务特定的分片逻辑。
  2. 范围分片算法 (RangeShardingAlgorithm)

    • 作用: 处理范围分片场景,即 SQL 条件中包含 BETWEEN ... AND ...>, <, >=, <= 操作符的分片键查询。通常与标准分片算法配合使用 (StandardShardingAlgorithm + RangeShardingAlgorithm = ClassBasedShardingAlgorithm)。
    • 核心接口:
      • doSharding(availableTargetNames, rangeShardingValue):根据传入的分片值范围计算目标数据源或表集合。
        • rangeShardingValue:包含分片键、逻辑表名和本次 SQL 中该分片键的值范围
    • 常用内置实现: 通常需要与 CLASS_BASED 结合,或者直接实现 RangeShardingAlgorithm 接口。
    • 示例场景:create_time 按月分表 (t_order_202301, t_order_202302…)。查询 WHERE create_time BETWEEN '2023-01-01' AND '2023-03-31' 时,范围算法需要计算出涉及 t_order_202301, t_order_202302, t_order_202303 三个表。
  3. 复合分片算法 (ComplexKeysShardingAlgorithm)

    • 作用: 当分片策略使用了多个分片键时,使用此算法处理包含多个分片键条件的复杂 SQL。
    • 核心接口:
      • doSharding(availableTargetNames, complexKeysShardingValues):根据传入的多个分片键的值(精确值或范围值)计算目标数据源或表集合。
        • complexKeysShardingValues:一个 Map,Key 是分片键名,Value 是该分片键对应的 ShardingValue 对象(可能是精确值列表或值范围)。
    • 实现方式: 通常需要自定义实现此接口,编写逻辑处理多个分片键的组合路由。内置无通用实现。
    • 示例场景: 同时按 tenant_idorder_type 分片。查询 WHERE tenant_id = 1 AND order_type IN (2, 3) 时,算法需要综合两个条件计算目标表。

三、 分片策略类型 (Sharding Strategy Type)

在配置层面,分片策略分为两种,它们都遵循 分片策略 = 分片键 + 分片算法 的公式,但作用域不同:

  1. 数据库分片策略 (databaseStrategy / defaultDatabaseStrategy)

    • 定义: 指定数据如何路由到不同的物理数据库 (DataSource) 的规则。
    • 配置元素:
      • shardingColumn(s): 库分片键(单个或多个)。
      • shardingAlgorithmName: 指向一个配置好的分片算法(可以是 StandardComplexClassBased 类型)。
    • 示例 (YAML):
      rules:
        - !SHARDING
          tables:
            t_order: # 逻辑表名
              actualDataNodes: ds${0..1}.t_order${0..15} # 物理数据节点 ds0.t_order0 ... ds1.t_order15
              databaseStrategy: # 数据库分片策略
                standard:
                  shardingColumn: user_id
                  shardingAlgorithmName: db_inline_mod
          shardingAlgorithms:
            db_inline_mod:
              type: INLINE
              props:
                algorithm-expression: ds${user_id % 2} # 按 user_id % 2 路由到 ds0 或 ds1
      
  2. 表分片策略 (tableStrategy / defaultTableStrategy)

    • 定义: 指定数据在同一个数据库内,如何路由到不同的物理表 (Table) 的规则。仅在分库分表或仅分表时配置。
    • 配置元素:
      • shardingColumn(s): 表分片键(单个或多个)。
      • shardingAlgorithmName: 指向一个配置好的分片算法。
    • 示例 (YAML - 接上例):
      tables:
        t_order:
          # ... actualDataNodes 同上 ...
          tableStrategy: # 表分片策略
            standard:
              shardingColumn: order_id
              shardingAlgorithmName: table_inline_mod
      shardingAlgorithms:
        # ... db_inline_mod 同上 ...
        table_inline_mod:
          type: INLINE
          props:
            algorithm-expression: t_order${order_id % 16} # 在同一个库内,按 order_id % 16 路由到 t_order0 到 t_order15
      

四、 绑定表 (Binding Table) 与广播表 (Broadcast Table)

虽然不是直接的分片策略,但它们与分片策略紧密相关,影响 SQL 路由和执行的效率:

  1. 绑定表 (Binding Table)

    • 定义: 指具有完全相同分库分表规则(即相同的分片键和分片算法)的主表和它的关联子表(如 t_ordert_order_item 都按 order_id 分片)。
    • 作用: 避免笛卡尔积关联查询。
    • 原理: 当执行 t_order JOIN t_order_item ON order_id 时,ShardingSphere-JDBC 知道它们的分片规则一致,只会将关联查询路由到具有相同 order_id 后缀的物理表上进行(如 t_order_0 JOIN t_order_item_0),而不是 t_order_0 JOIN 所有 t_order_item_x
    • 配置 (YAML):
      rules:
        - !SHARDING
          bindingTables:
            - t_order, t_order_item # 声明 t_order 和 t_order_item 是绑定表关系
      
  2. 广播表 (Broadcast Table)

    • 定义: 指存在于所有分片库中数据量小且更新不频繁的表(如字典表 t_dict、配置表 t_config)。
    • 作用: 简化关联查询,避免跨库 JOIN。
    • 原理: 对广播表的任何操作(INSERT/UPDATE/DELETE/SELECT)都会被广播到所有分片库上执行。查询时,与广播表 JOIN 的操作可以在本地库内完成
    • 配置 (YAML):
      rules:
        - !SHARDING
          broadcastTables:
            - t_config # 声明 t_config 是广播表
            - t_region
      

五、 配置详解 (YAML 示例)

一个包含分库分表策略、绑定表、广播表的较完整配置示例:

rules:
  - !SHARDING
    # 1. 数据节点定义 (物理库.物理表)
    tables:
      t_order: # 逻辑表名
        actualDataNodes: ds${0..1}.t_order${0..15} # 物理节点: ds0.t_order0, ds0.t_order1 ... ds1.t_order15
        # 2. 数据库分片策略 (路由到哪个库)
        databaseStrategy:
          standard:
            shardingColumn: user_id # 库分片键
            shardingAlgorithmName: db_inline_mod # 使用的库分片算法
        # 3. 表分片策略 (在同一个库内路由到哪个表)
        tableStrategy:
          standard:
            shardingColumn: order_id # 表分片键
            shardingAlgorithmName: table_inline_mod # 使用的表分片算法
      t_order_item: # 子表
        actualDataNodes: ds${0..1}.t_order_item${0..15}
        databaseStrategy:
          standard:
            shardingColumn: user_id # 必须与主表 t_order 的库分片键和算法一致
            shardingAlgorithmName: db_inline_mod
        tableStrategy:
          standard:
            shardingColumn: order_id # 必须与主表 t_order 的表分片键和算法一致
            shardingAlgorithmName: table_inline_mod
    # 4. 绑定表声明 (优化关联查询)
    bindingTables:
      - t_order, t_order_item # 声明绑定关系
    # 5. 广播表声明
    broadcastTables:
      - t_config # 配置表
      - t_region # 地区表
    # 6. 分片算法定义
    shardingAlgorithms:
      db_inline_mod: # 库分片算法 (INLINE 表达式)
        type: INLINE
        props:
          algorithm-expression: ds${user_id % 2} # 根据 user_id 模 2 选择库 (ds0/ds1)
      table_inline_mod: # 表分片算法 (INLINE 表达式)
        type: INLINE
        props:
          algorithm-expression: t_order${order_id % 16} # 根据 order_id 模 16 选择表 (t_order0 - t_order15)
    # 7. 默认策略 (可选,如果表未单独配置策略则使用此默认)
    defaultDatabaseStrategy:
      none: # 例如,所有表都在同一个库,则不需要分库策略
    defaultTableStrategy:
      none:

六、 选择分片策略的关键考虑因素

  1. 数据分布均匀性: 确保分片键值分布均匀,避免数据倾斜(某些库/表数据量巨大,某些空闲)。
  2. 查询模式:
    • 高频查询条件是否包含分片键?包含则能精准路由。
    • 是否需要范围查询?需要则选择支持范围分片的算法。
    • 是否有复杂多条件查询?可能需要复合分片或Hint。
  3. 业务增长: 分片策略是否易于扩容?取模算法扩容较麻烦(需要数据迁移),范围、时间分片扩容相对容易。
  4. 事务与关联查询:
    • 跨分片事务复杂,尽量让相关数据在同一分片(如绑定表)。
    • 避免非分片键上的复杂 JOIN 和 GROUP BY,性能差。
  5. 分片键选择: 是策略成功的基础。选择基数大、业务常用、分布均匀的字段。

七、 常见问题与陷阱

  1. 分片键不在查询条件中: 导致全库全表路由 (SELECT * FROM t_order),性能灾难!务必确保核心查询包含分片键。
  2. 分片键值不均衡: 导致数据倾斜。需选择分布均匀的键或调整算法(如一致性哈希)。
  3. 扩容麻烦(取模算法): % 2 扩容到 % 4 需要数据迁移。考虑范围分片或一致性哈希。
  4. 跨分片复杂查询性能差: 如非分片键上的 ORDER BY ... LIMITDISTINCTGROUP BY、跨分片 JOIN。需业务设计规避或接受性能损耗。
  5. 分布式事务: 跨分片更新需要分布式事务支持(XA/Seata等),增加复杂度和性能开销。
  6. 分布式主键: 需使用分布式ID生成器(如Snowflake, UUID)代替数据库自增ID,避免冲突。
  7. 分页查询陷阱: LIMIT 10 OFFSET 100 在分片环境下含义不同(每个分片取前110条,再归并取10条),效率低且结果可能不稳定。尽量使用连续翻页(WHERE id > last_max_id LIMIT 10)。

总结: ShardingSphere-JDBC 的分片策略是其最核心的配置。理解 分片键 + 分片算法 的构成,熟练掌握 StandardRangeComplex 算法的应用场景,合理配置 databaseStrategytableStrategy,并善用 绑定表广播表 优化查询,是构建高效、可扩展分库分表系统的关键。务必结合业务场景谨慎选择分片键和算法,并充分意识到分片带来的查询限制和挑战。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值