MySQL分库分表实战:ShardingSphere在物流订单系统中的落地方案

举报
江南清风起 发表于 2025/07/20 20:30:55 2025/07/20
【摘要】 MySQL分库分表实战:ShardingSphere在物流订单系统中的落地方案关键词:MySQL、ShardingSphere、分库分表、物流订单、Snowflake、分布式事务、Spring Boot 目录业务背景与挑战技术选型与整体架构数据建模与分片策略环境准备与建表脚本Spring Boot + ShardingSphere-JDBC 集成分布式主键 & 雪花算法落地复杂业务 SQL...

MySQL分库分表实战:ShardingSphere在物流订单系统中的落地方案

关键词:MySQL、ShardingSphere、分库分表、物流订单、Snowflake、分布式事务、Spring Boot

目录

  1. 业务背景与挑战
  2. 技术选型与整体架构
  3. 数据建模与分片策略
  4. 环境准备与建表脚本
  5. Spring Boot + ShardingSphere-JDBC 集成
  6. 分布式主键 & 雪花算法落地
  7. 复杂业务 SQL 改写与测试
  8. 分布式事务 —— Seata 方案
  9. 运维:弹性扩缩容 & 灰度方案
  10. 性能压测与最佳实践总结

1. 业务背景与挑战

某头部物流平台日均订单 800W,峰值 2.2KW。单库单表(t_order)在 6 亿数据时:

  • 写入 RT P99 > 400 ms(索引维护、锁竞争)。
  • 大促期间磁盘 I/O 100%,MySQL QPS 天花板 6K。
  • DDL 无法在线执行(GH-OST 时长 > 18 h)。

=> 必须水平拆分;业务特点:

  • 查询维度:订单号、用户 ID、运单号。
  • 生命周期:订单完结 30 天后迁移冷存,热数据占比 < 20%。

2. 技术选型与整体架构

维度 选项 理由
分片中间件 ShardingSphere-JDBC 5.4.1 轻量、无中心化 Proxy、与 Spring 生态深度整合
分片算法 复合分片 用户 ID % 8 分库,订单号 Hash % 32 分表
分布式主键 Snowflake 支持 64 bit 趋势递增,避免 Page 热点
分布式事务 Seata AT 对业务代码侵入极低
冷存 TiFlash / ClickHouse OLAP 场景下沉

架构图:

┌──────────────┐      ┌──────────────┐
│  Spring Boot │──────│Sharding-JDBC │
└──────┬───────┘      └──────┬───────┘
       │ 8*主库            │
       │ 8*从库            │
 ┌─────┴─────┐       ┌─────┴─────┐
 │ ds0 ~ ds7 │       │ ck 冷存   │
 └───────────┘       └───────────┘

3. 数据建模与分片策略

3.1 逻辑表结构

CREATE TABLE t_order (
  order_id     BIGINT PRIMARY KEY,
  user_id      BIGINT NOT NULL,
  waybill_no   VARCHAR(32) NOT NULL,
  status       TINYINT NOT NULL,
  created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_user (user_id),
  INDEX idx_waybill (waybill_no)
) ENGINE=InnoDB;

3.2 分片规则

  • 分库键user_id % 8ds${0..7}
  • 分表键order_id & 31t_order_${0..31}
  • 绑定表t_order_itemt_order_trackt_order 使用相同的 order_id 路由,避免跨分片 JOIN。

4. 环境准备与建表脚本

4.1 一键建库建表(Shell)

for i in {0..7}; do
  mysql -h127.0.0.1 -P3306 -uroot -p123456 -e "
    CREATE DATABASE IF NOT EXISTS logistics_${i};
    USE logistics_${i};
    $(cat create_table.sql | sed 's/t_order/t_order_&/g')
  "
done

create_table.sql 里仅包含 t_order 建表语句,通过 sed 替换生成 32 张分表。


5. Spring Boot + ShardingSphere-JDBC 集成

5.1 Maven 依赖

<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  <version>5.4.1</version>
</dependency>

5.2 application.yml

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1,...,ds7
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        jdbcUrl: jdbc:mysql://127.0.0.1:3306/logistics_0?useSSL=false
        username: root
        password: 123456
      # ... ds1 ~ ds7 同上
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..7}.t_order_$->{0..31}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order-inline
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: user-inline
        binding-tables: t_order,t_order_item,t_order_track
        sharding-algorithms:
          order-inline:
            type: INLINE
            props:
              algorithm-expression: t_order_$->{order_id & 31}
          user-inline:
            type: INLINE
            props:
              algorithm-expression: ds$->{user_id % 8}
    props:
      sql-show: true

5.3 读写分离(可选)

spring:
  shardingsphere:
    rules:
      readwrite-splitting:
        data-sources:
          ds0-rw:
            type: Static
            props:
              write-data-source-name: ds0
              read-data-source-names: ds0-slave0,ds0-slave1

6. 分布式主键 & 雪花算法落地

ShardingSphere 已内置 Snowflake,无需额外配置:

@TableId(value = "order_id", type = IdType.ASSIGN_ID) // MyBatis-Plus
private Long orderId;

注意:Snowflake 依赖 worker-id,多实例部署需通过 spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=${HOST_NAME_HASH} 防止重复。


7. 复杂业务 SQL 改写与测试

7.1 分页查询用户最近 30 天订单

Page<OrderDO> page = new Page<>(1, 10);
QueryWrapper<OrderDO> qw = new QueryWrapper<>();
qw.eq("user_id", 123456L)
  .ge("created_time", LocalDateTime.now().minusDays(30))
  .orderByDesc("created_time");

IPage<OrderDO> result = orderMapper.selectPage(page, qw);
  • 路由结果user_id % 8 = 4,只扫 ds4 的 32 张分表,避免全库广播。
  • 分页优化:使用 分片键 + 时间索引 覆盖查询,避免 filesort。

7.2 运单号反查订单

运单号非分片键 → 需 基因法 改造:

-- 新增冗余字段 waybill_hash = CRC32(waybill_no) & 31
SELECT * FROM t_order_${waybill_hash} WHERE waybill_no = 'SF123456789';

保证单表定位,99.9% 查询 < 5 ms。


8. 分布式事务 —— Seata 方案

8.1 引入 Seata

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

8.2 全局事务示例

@Service
public class OrderService {

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public Long createOrder(CreateOrderDTO dto) {
        // 1. 插入 t_order
        orderMapper.insert(order);
        // 2. 扣减库存 (RPC -> stock-service)
        stockFeign.deduct(dto.getSkuId(), dto.getNum());
        return order.getOrderId();
    }
}

Seata AT 模式对业务零侵入,undo log 存储在分库中,需在每个库建 undo_log 表。


9. 运维:弹性扩缩容 & 灰度方案

场景 方案 工具
扩容库 双写 + 校验 + 切流 自研 migrator + Flink CDC
扩容表 一致性哈希环 shardingsphere-scaling
灰度切流 ShardingSphere Hint 通过 HintManager 强制路由影子库

10. 性能压测与最佳实践总结

  • 压测结果(16C32G * 8 库):
    • 写入 QPS 8.7W,P99 延迟 18 ms。
    • 查询 QPS 12W,P99 延迟 9 ms。
  • 经验清单
    1. 永远带分片键查询,否则走全库广播。
    2. 避免热点账户(user_id 连续),可在业务层加随机扰动。
    3. 定期清理过期分区,减少索引树高度。
    4. 监控 information_schema.INNODB_METRICS,及时发现行锁等待。

image.png

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。