分库分表方案落地:ShardingSphere实战配置指南

单表数据量超过千万级后,查询性能下降明显,索引维护成本上升。ShardingSphere作为Apache顶级项目,提供分库分表、读写分离和数据加密等能力,对应用层透明。本文以Java Spring Boot项目为例,完整演示分库分表方案的落地配置。

分片策略设计与数据分布规划

分片策略包含分片键选择和分片算法两部分。分片键应选择查询条件中出现频率最高的字段,避免跨片查询。订单系统通常以user_id作为分片键,保证同一用户的订单落在同一数据节点。

分片规模规划:假设单表500万行作为阈值,当前订单量约8000万,按user_id取模分16库16表,单表数据约30万行,满足性能要求。预留3倍增长空间。

ShardingSphere-JDBC集成配置

Maven依赖引入:

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core</artifactId>
    <version>5.4.1</version>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>4.0.3</version>
</dependency>

application.yml分库分表配置:

spring:
  shardingsphere:
    mode:
      type: Standalone
      repository:
        type: JDBC
    datasource:
      names: ds0,ds1,ds2,ds3
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://mysql-0:3306/order_db_0?useSSL=false&characterEncoding=utf8
        username: root
        password: ${DB_PASSWORD}
        pool-name: HikariCP-ds0
        maximum-pool-size: 20
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://mysql-1:3306/order_db_1?useSSL=false&characterEncoding=utf8
        username: root
        password: ${DB_PASSWORD}
        maximum-pool-size: 20
      ds2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://mysql-2:3306/order_db_2?useSSL=false&characterEncoding=utf8
        username: root
        password: ${DB_PASSWORD}
        maximum-pool-size: 20
      ds3:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://mysql-3:3306/order_db_3?useSSL=false&characterEncoding=utf8
        username: root
        password: ${DB_PASSWORD}
        maximum-pool-size: 20

    rules:
      # 分库分表规则
      sharding:
        tables:
          t_order:
            # 真实数据节点:4库 x 4表 = 16个分片
            actual-data-nodes: ds${0..3}.t_order_${0..3}
            # 数据库分片策略
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: db-mod-hash
            # 表分片策略
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: table-mod-hash
            # 分布式主键
            key-generate-strategy:
              column: order_id
              key-generator-name: snowflake

          # 订单明细表 - 绑定到订单表
          t_order_item:
            actual-data-nodes: ds${0..3}.t_order_item_${0..3}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: db-mod-hash
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: table-mod-hash
            key-generate-strategy:
              column: item_id
              key-generator-name: snowflake

        # 绑定表关系(避免跨库JOIN)
        binding-tables:
          - t_order,t_order_item

        # 广播表(字典表,所有库都有全量数据)
        broadcast-tables:
          - t_dict_order_status
          - t_dict_payment_type

        # 分片算法定义
        sharding-algorithms:
          db-mod-hash:
            type: MOD
            props:
              sharding-count: 4
          table-mod-hash:
            type: MOD
            props:
              sharding-count: 4

        # 分布式主键生成器
        key-generators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 1

      # 读写分离规则
      readwrite-splitting:
        data-sources:
          readwrite_ds0:
            write-data-source-name: ds0
            read-data-source-names: ds0-read0,ds0-read1
            load-balancer-name: round-robin
          readwrite_ds1:
            write-data-source-name: ds1
            read-data-source-names: ds1-read0,ds1-read1
            load-balancer-name: round-robin
        load-balancers:
          round-robin:
            type: ROUND_ROBIN

    props:
      sql-show: true  # 打印实际执行的SQL(生产环境关闭)

分布式主键与跨片查询处理

ShardingSphere内置Snowflake算法生成分布式ID,配置worker-id确保集群中各节点不重复。Java实体类中标注主键生成策略:

@Data
@TableName("t_order")
public class Order {
    @TableId(type = IdType.ASSIGN_ID)  // 使用ShardingSphere生成
    private Long orderId;

    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    private Integer status;
    private LocalDateTime createTime;
}

// MyBatis-Plus中无需额外配置,ShardingSphere拦截SQL自动路由
// 指定user_id的查询 - 精准路由到单分片
// SELECT * FROM t_order WHERE user_id = 10086 AND status = 1
// → SELECT * FROM t_order_2 ON ds2 WHERE user_id = 10086 AND status = 1

// 未指定user_id的查询 - 全分片扫描(生产环境避免)
// SELECT * FROM t_order WHERE status = 1 ORDER BY create_time DESC LIMIT 20
// → 16个分片各执行 LIMIT 20,归并排序后取前20

跨片查询优化策略:

// 1. 带分片键的查询 - 单分片路由
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Order::getUserId, userId)
       .eq(Order::getStatus, 1)
       .orderByDesc(Order::getCreateTime);
List orders = orderMapper.selectList(wrapper);

// 2. 二级索引表 - 非分片键查询场景
// 建立order_no到user_id的映射表(不分片)
@TableName("t_order_no_mapping")
public class OrderNoMapping {
    private String orderNo;  // 订单号
    private Long userId;     // 分片键
    private Long orderId;    // 主键
}

// 先查映射表获取user_id,再精准路由
public Order findByOrderNo(String orderNo) {
    OrderNoMapping mapping = mappingMapper.selectByOrderNo(orderNo);
    if (mapping == null) return null;
    return orderMapper.selectByUserIdAndOrderId(
        mapping.getUserId(), mapping.getOrderId());
}

// 3. 分页查询 - 使用游标避免深度分页
// 上一页最后一条记录的create_time作为游标
wrapper.lt(Order::getCreateTime, lastCreateTime)
       .orderByDesc(Order::getCreateTime)
       .last("LIMIT 20");

数据迁移与扩容方案

从单库迁移到分库分表环境,采用双写加数据同步方式:

// 迁移步骤:
// 1. 应用层开启双写(新数据同时写旧库和新分片库)
// 2. 使用DataX/Canal同步存量数据到新分片库
// 3. 数据校验工具比对一致性
// 4. 灰度切读 - 按用户ID取模逐步切到新库
// 5. 全量切读后下线旧库双写

// Canal监听Binlog同步配置示例
canal:
  instance:
    master:
      address: mysql-old:3306
      journal: ""
      position: ""
    filter: "old_db.t_order"
  sink:
    type: sharding  # 写入ShardingSphere分片库

扩容场景(4库扩到8库)使用一致性Hash算法替代取模,减少数据迁移量。或采用2的幂次扩容(4→8→16),每次扩容只需迁移一半数据。ShardingSphere 5.x支持在线变更分片规则,配合数据同步工具实现平滑扩容。

分布式事务与运维监控

ShardingSphere提供LOCAL、XA、BASE三种事务模式。跨库写操作推荐使用Seata AT模式(BASE事务):

// application.yml添加Seata配置
seata:
  tx-service-group: order-service-group
  service:
    vgroup-mapping:
      order-service-group: default
  registry:
    type: nacos
    nacos:
      server-addr: nacos:8848

// 业务代码中使用@GlobalTransactional
@Service
public class OrderService {
    @GlobalTransactional
    public void createOrder(OrderDTO dto) {
        // 写入ds0的订单表
        orderMapper.insert(buildOrder(dto));
        // 写入ds2的库存表(跨库)
        inventoryMapper.deduct(dto.getSkuId(), dto.getQty());
        // 写入ds1的积分表(跨库)
        pointMapper.add(dto.getUserId(), calculatePoints(dto));
    }
}

监控方面,ShardingSphere暴露metrics到Prometheus,关注以下指标:每秒SQL路由数、跨片查询比例、连接池使用率、慢查询分布。跨片查询比例持续高于10%时,需要检查分片键设计或增加二级索引。

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

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

相关推荐