SQL分库分表实战:ShardingSphere分片策略与分布式查询路由配置详解

分库分表的架构选型

单库单表在数据量达到千万级时面临查询性能下降、写入瓶颈、备份恢复时间长等问题。分库分表通过将数据水平拆分到多个数据库实例和表文件中,降低单节点数据量,提升整体吞吐能力。ShardingSphere-JDBC作为客户端分片方案,无需独立中间件部署,在应用层完成SQL路由和结果归并。

分片策略的核心是分片键(Sharding Key)的选择。分片键应满足三个条件:查询条件中高频出现的字段、取值分布均匀的字段、业务上不可变或极少变更的字段。订单系统通常选择user_id或order_id作为分片键,以用户维度或订单维度拆分数据。

Spring Boot集成ShardingSphere-JDBC

Maven依赖配置:

<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>shardingsphere-jdbc</artifactId>
  <version>5.5.1</version>
</dependency>

application.yml配置分库分表规则:

spring:
  datasource:
    driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
    url: jdbc:shardingsphere:classpath:sharding-config.yaml

sharding:
  datasource:
    names: ds0,ds1
    ds0:
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://10.0.0.1:3306/order_db0
      username: root
      password: password
    ds1:
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://10.0.0.2:3306/order_db1
      username: root
      password: password
  
  tables:
    t_order:
      actual-data-nodes: ds${0..1}.t_order${0..3}
      database-strategy:
        standard:
          sharding-column: user_id
          sharding-algorithm-name: db-mod
      table-strategy:
        standard:
          sharding-column: order_id
          sharding-algorithm-name: table-mod
      key-generate-strategy:
        column: order_id
        key-generator-name: snowflake
  
  sharding-algorithms:
    db-mod:
      type: MOD
      props:
        sharding-count: 2
    table-mod:
      type: MOD
      props:
        sharding-count: 4
  
  key-generators:
    snowflake:
      type: SNOWFLAKE
      props:
        worker-id: 1

以上配置将t_order表拆分为2个数据库各4张表共8个分片。user_id作为分库键,取模2路由到ds0或ds1;order_id作为分表键,取模4路由到t_order0至t_order3。订单ID使用雪花算法生成,保证全局唯一和递增性。

分布式主键生成策略

ShardingSphere内置三种主键生成策略:SNOWFLAKE(雪花算法)、UUID和NANOID。雪花算法通过时间戳加机器ID加序列号组合生成64位整数,兼具全局唯一性和有序性,是分库分表场景的首选方案。

// 雪花算法结构(64位)
// | 1位符号 | 41位时间戳 | 10位机器ID | 12位序列号 |
// 理论上每台机器每毫秒可生成4096个ID

// 自定义主键生成器
public class CustomKeyGenerator implements KeyGenerateAlgorithm {
    private final AtomicLong counter = new AtomicLong(0);
    
    @Override
    public Comparable<?> generateKey() {
        long timestamp = System.currentTimeMillis();
        long workerId = getWorkerId();
        long sequence = counter.getAndIncrement() & 0xFFF;
        return (timestamp << 22) | (workerId << 12) | sequence;
    }
    
    @Override
    public String getType() {
        return "CUSTOM";
    }
}

部署多实例时需确保worker-id不同,否则会产生主键冲突。可通过Zookeeper或Nacos协调分配worker-id,或使用机器IP末段作为worker-id的简单方案。

跨分片查询与结果归并

分库分表后,非分片键查询需要广播到所有分片执行再归并结果。ShardingSphere自动处理广播查询和结果归并,但查询性能取决于分片数量和归并排序复杂度。

-- 按分片键查询 - 精准路由到单个分片
SELECT * FROM t_order WHERE user_id = 1001 AND order_id = 50001;
-- 路由到 ds1.t_order1

-- 按非分片键查询 - 广播到所有分片
SELECT * FROM t_order WHERE status = 'PAID' ORDER BY create_time DESC LIMIT 20;
-- 路由到所有8个分片,各分片取20条,归并后取Top20

-- 分页查询的深度分片问题
SELECT * FROM t_order ORDER BY create_time DESC LIMIT 10000, 20;
-- 需要各分片取10020条归并,深度分页时性能急剧下降

深度分页问题的优化方案:使用游标分页替代OFFSET分页,记录上一页最后一条记录的排序键值作为查询条件:

-- 传统分页(深度分页性能差)
SELECT * FROM t_order ORDER BY create_time DESC LIMIT 10000, 20;

-- 游标分页(性能稳定)
SELECT * FROM t_order 
WHERE create_time < '2026-08-24 10:30:00' 
ORDER BY create_time DESC 
LIMIT 20;

广播表与绑定表配置

字典表、配置表等数据量小但需频繁关联查询的表配置为广播表,在所有数据节点存储完整副本,JOIN查询时无需跨库:

sharding:
  broadcast-tables:
    - t_dict
    - t_config
  
  binding-tables:
    - t_order,t_order_item

绑定表(Binding Table)指具有相同分片策略的主子表,如t_ordert_order_itemorder_id分片。配置为绑定表后,两者的JOIN查询在同一分片内完成,避免笛卡尔积式的跨库关联。

分布式事务处理

跨库操作需要分布式事务保证数据一致性。ShardingSphere支持三种事务类型:LOCAL(本地事务)、XA(两阶段提交)和BASE(柔性事务)。

// XA事务配置
spring:
  shardingsphere:
    props:
      xa-transaction-manager-type: Atomikos

// 代码中使用
@Transactional
public void createOrder(OrderDTO dto) {
    // 订单写入ds0
    orderMapper.insert(dto);
    // 订单详情写入ds1(与订单分片键一致则同库)
    orderItemMapper.insertBatch(dto.getItems());
    // 库存扣减可能在不同库
    stockMapper.deduct(dto.getSkuId(), dto.getQuantity());
}

XA事务保证强一致性但性能开销大,适合金融场景。BASE事务通过Seata AT模式实现最终一致性,性能更高,适合电商等容忍短暂不一致的场景。选择事务策略时需在一致性和性能之间权衡。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/sql-fen-ku-fen-biao-shi-zhan-shardingsphere-fen-pian-ce/

(0)
小编小编
上一篇 2小时前
下一篇 2小时前

相关推荐