10亿订单如何做分库分表?

大家好,我是苏三,又跟大家见面了。

前言

场景痛点:某电商平台的MySQL订单表达到7亿行时,出现致命问题:

sql 复制代码
-- 简单查询竟需12秒!
SELECT * FROM orders WHERE user_id=10086 LIMIT 10;

-- 统计全表耗时278秒
SELECT COUNT(*) FROM orders;

核心矛盾

  1. B+树索引深度达到5层,磁盘IO暴增。
  2. 单表超200GB导致备份时间窗突破6小时。
  3. 写并发量达8000QPS,主从延迟高达15分钟。

关键认知 :当单表数据量突破5000万行时,就该启动分库分表设计预案。

那么问题来了,假如现在有10亿的订单数据,我们该如何做分库分表呢?

今天这篇文章就跟大家一起聊聊这个问题,希望对你会有所帮助。

最近准备面试的小伙伴,可以看一下这个宝藏网站(Java突击队):www.susan.net.cn,里面:面试八股文、场景题、面试真题、项目实战、工作内推什么都有

1 分库分表核心策略

1.1 垂直拆分:先给数据做减法

优化效果

  • 核心表体积减少60%
  • 高频查询字段集中提升缓存命中率

1.2 水平拆分:终极解决方案

分片键选择三原则

  1. 离散性:避免数据热点(如user_id优于status)
  2. 业务相关性:80%查询需携带该字段
  3. 稳定性:值不随业务变更(避免使用手机号)

分片策略对比

策略类型 适用场景 扩容复杂度 示例
范围分片 带时间范围的查询 简单 create_time按月分表
哈希取模 均匀分布 困难 user_id % 128
一致性哈希 动态扩容 中等 使用Ketama算法
基因分片 避免跨分片查询 复杂 从user_id提取分库基因

2 基因分片

针对订单系统的三大高频查询:

  1. 用户查历史订单(user_id)
  2. 商家查订单(merchant_id)
  3. 客服按订单号查询(order_no)

解决方案

Snowflake订单ID改造

java 复制代码
// 基因分片ID生成器
publicclass OrderIdGenerator {
    // 64位ID结构:符号位(1)+时间戳(41)+分片基因(12)+序列号(10)
    privatestaticfinalint GENE_BITS = 12;
    
    public static long generateId(long userId) {
        long timestamp = System.currentTimeMillis() - 1288834974657L;
        // 提取用户ID后12位作为基因
        long gene = userId & ((1 << GENE_BITS) - 1); 
        long sequence = ... // 获取序列号
        
        return (timestamp << 22) 
             | (gene << 10) 
             | sequence;
    }
    
    // 从订单ID反推分片位置
    public static int getShardKey(long orderId) {
        return (int) ((orderId >> 10) & 0xFFF); // 提取中间12位
    }
}

路由逻辑

ini 复制代码
// 分库分表路由引擎
publicclass OrderShardingRouter {
    // 分8个库 每个库16张表
    privatestaticfinalint DB_COUNT = 8; 
    privatestaticfinalint TABLE_COUNT_PER_DB = 16;
    
    public static String route(long orderId) {
        int gene = OrderIdGenerator.getShardKey(orderId);
        int dbIndex = gene % DB_COUNT;
        int tableIndex = gene % TABLE_COUNT_PER_DB;
        
        return"order_db_" + dbIndex + ".orders_" + tableIndex;
    }
}

关键突破:通过基因嵌入,使相同用户的订单始终落在同一分片,同时支持通过订单ID直接定位分片

3 跨分片查询

3.1 异构索引表方案

Elasticsearch索引表结构

json 复制代码
{
  "order_index": {
    "mappings": {
      "properties": {
        "order_no": { "type": "keyword" },
        "shard_key": { "type": "integer" },
        "create_time": { "type": "date" }
      }
    }
  }
}

4.2 全局二级索引(GSI)

scss 复制代码
-- 在ShardingSphere中创建全局索引
CREATE SHARDING GLOBAL INDEX idx_merchant ON orders(merchant_id) 
    BY SHARDING_ALGORITHM(merchant_hash) 
    WITH STORAGE_UNIT(ds_0,ds_1);

最近建了一些工作内推群,各大城市都有,欢迎各位HR和找工作的小伙伴进群交流,群里目前已经收集了不少的工作内推岗位。加苏三的微信:li_su223,备注:掘金+所在城市,即可进群。

4、数据迁移

双写迁移方案

灰度切换步骤

  1. 开启双写(新库写失败需回滚旧库)
  2. 全量迁移历史数据(采用分页批处理)
  3. 增量数据实时校验(校验不一致自动修复)
  4. 按用户ID灰度流量切换(从1%到100%)

5、避坑指南

5.1 热点问题

双十一期间发现某网红店铺订单全部分到同一分片。

解决方案:引入复合分片键 (merchant_id + user_id) % 1024

5.2 分布式事务

这里的分布式事务使用的RocketMQ的数据最终一致性方案:

typescript 复制代码
// 最终一致性方案
@Transactional
public void createOrder(Order order) {
   orderDao.insert(order); // 写主库
   rocketMQTemplate.sendAsync("order_create_event", order); // 发消息
}

// 消费者处理
@RocketMQMessageListener(topic = "order_create_event")
public void handleEvent(OrderEvent event) {
   bonusService.addPoints(event.getUserId()); // 异步加积分
   inventoryService.deduct(event.getSkuId()); // 异步扣库存
}

5.3 分页陷阱

跨分片查询页码错乱。

解决方案:改用ES聚合查询或业务折衷方案(只查最近3个月订单)。

6 终极架构方案

性能指标

场景 拆分前 拆分后
用户订单查询 3200ms 68ms
商家订单导出 超时失败 8s完成
全表统计 不可用 1.2s(近似)

总结

  1. 分片键选择大于努力:基因分片是订单系统的最佳拍档。
  2. 扩容预留空间:建议初始设计支持2年数据增长。
  3. 避免过度设计:小表关联查询远比分布式Join高。效
  4. 监控驱动优化:重点关注分片倾斜率>15%的库。

真正的架构艺术,是在分与合之间找到平衡点。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

本文收录于我的技术网站:www.susan.net.cn

相关推荐
㳺三才人子3 小时前
初探 Flask
后端·python·flask·html
星栈独行3 小时前
我在 Rust 全栈项目里用 JWT 做无状态认证
开发语言·后端·rust·前端框架·开源·github·web
Java爱好狂.4 小时前
Java程序员体系化学习路线(2026最新版)
java·后端·java面试·java架构师·java程序员·java八股文·java学习路线
陈随易4 小时前
Redis 8.8发布,一定要更新
前端·后端·程序员
装不满的克莱因瓶4 小时前
SpringBoot 如何将 lib 目录中jar包打包进最终的jar包里面
spring boot·后端·maven·jar·mvn
ltl5 小时前
Transformer 原论文实验结果:为什么 28.4 BLEU 足以改写路线图
后端
excel6 小时前
为什么我推荐使用 Termius:现代 SSH 工具的完整体验
前端·后端
卷毛的技术笔记6 小时前
Java后端硬核实战:用Spring AI Alibaba+Redis给LLM装上“超强记忆中枢”
java·人工智能·redis·后端·spring·ai·系统架构
IT_陈寒7 小时前
Java的Optional差点让我掉坑里,这几个坑你别踩
前端·人工智能·后端
子兮曰8 小时前
Harness 驾驭工程深度教程:从 AGENTS.md 到全链路 AI 编码基础设施
前端·后端·ai编程