文章目录
-
- 题目
- 一、方案设计背景与核心挑战
- 二、分库分表核心设计
-
- [1. 拆分维度选择:复合分片(时间+用户ID)](#1. 拆分维度选择:复合分片(时间+用户ID))
- [2. 分库分表中间件选型:Sharding-JDBC](#2. 分库分表中间件选型:Sharding-JDBC)
- 三、热/冷数据分层存储与查询优化
-
- [1. 热数据存储:高性能集群](#1. 热数据存储:高性能集群)
- [2. 冷数据存储:低成本归档集群](#2. 冷数据存储:低成本归档集群)
- [3. 查询优化方案](#3. 查询优化方案)
- 四、订单-库存强一致性保障
-
- [1. 核心流程(TCC三阶段)](#1. 核心流程(TCC三阶段))
- [2. 最终一致性补偿](#2. 最终一致性补偿)
- 五、无感扩容方案
-
- [1. 扩容前准备](#1. 扩容前准备)
- [2. 灰度迁移数据](#2. 灰度迁移数据)
- [3. 扩容完成](#3. 扩容完成)
- 六、性能压测与容量规划
-
- [1. 性能压测指标(大促峰值)](#1. 性能压测指标(大促峰值))
- [2. 容量规划(未来3年)](#2. 容量规划(未来3年))
- 七、总结
题目
一个电商平台,去年订单量3亿,预计今年及后两年每年订单量10亿。帮忙设计一个分库分表方案,需要满足以下要求:
- 用户可查最近三个月订单,运营需要全量订单做统计报表
- 交易订单和库存必须保证强一致性或可补偿。
- 设计合理扩容方案,保障用户无感扩容。
- 大促时,读QPS峰值50000,写QPS峰值10000。
一、方案设计背景与核心挑战
- 数据规模:当前年订单量3亿,未来3年年均10亿,3年累计订单量约33亿,单表存储极限(1000万行)下需拆分330+表,需通过分库分表解决单库单表性能瓶颈。
- 核心需求 :
- 查询场景:用户高频查询最近3个月订单(热数据),运营低频查询全量订单(冷数据);
- 一致性要求:订单交易与库存扣减强一致或可补偿;
- 性能目标:大促读QPS峰值50000、写QPS峰值10000;
- 扩容要求:支持无感扩容,避免业务中断。
二、分库分表核心设计
1. 拆分维度选择:复合分片(时间+用户ID)
(1)主分片维度:时间(按季度分表)
- 理由:用户查询集中在"最近3个月",按时间拆分可天然隔离热/冷数据,降低热表数据量(单季度订单量约2.5亿,按用户ID二次拆分后单表数据量可控)。
- 拆分规则 :
- 表名格式:
order_{年份}{季度}
(如order_2024Q1
、order_2024Q2
);- 热数据范围:当前季度+前2个季度(共3个月),部署在高性能存储节点;
- 冷数据范围:3个月前的历史订单,迁移至低成本存储(如阿里云Lindorm、AWS Aurora冷存储),仅支持运营统计查询。
(2)二次分片维度:用户ID哈希(分库分表)
- 理由:同一用户的订单需聚合查询(如"我的全部订单"),按用户ID哈希拆分可保证用户订单落在同一分表,避免跨表聚合;同时均匀分摊写压力(写QPS峰值10000需分散到多节点)。
- 拆分规则 :
- 分库数量 :初期8个库(
db_order_0
~db_order_7
),每个库承载写QPS约1250,预留扩容空间(未来可扩至16/32库);- 分表数量 :每个库内按用户ID哈希拆分为16张表(
order_{年份}{季度}_0
~order_{年份}{季度}_15
),单表季度数据量≈2.5亿/(8库×16表)=195万行,远低于1000万行阈值;- 分片公式 :
- 库索引:
user_id % 8
(初期),未来扩容时调整为user_id % 16
(通过一致性哈希平滑过渡);- 表索引:
user_id / 8 % 16
(确保同一用户在同一库内的表聚合)。
2. 分库分表中间件选型:Sharding-JDBC
- 选型理由 :
- 轻量级客户端方案,无独立中间件部署成本,避免网络转发损耗(相比MyCat减少1次网络开销,适配读QPS 50000的性能需求);
- 支持动态分片配置(通过配置中心实时更新分片规则),满足无感扩容;
- 内置分布式事务(TCC/XA)、读写分离、缓存等功能,可直接对接订单-库存一致性需求。
- 核心配置 :
- 读写分离:每个分库配置1主2从,读请求路由到从库(分担50000读QPS,主库仅处理写请求);
- 分片策略:时间分片(按订单创建时间
create_time
)+ 用户ID哈希分片(按user_id
),复合分片算法通过Sharding-JDBC自定义实现。
三、热/冷数据分层存储与查询优化
1. 热数据存储:高性能集群
- 存储节点 :采用MySQL 8.0主从架构(1主2从),主库使用SSD磁盘(写性能提升3倍),从库开启并行复制(
slave_parallel_workers=8
),降低主从延迟(控制在100ms内);- 索引优化 :
- 主键:
order_id
(雪花算法生成,包含时间戳,便于按时间范围查询);- 联合索引:
user_id + create_time
(优化用户查询最近3个月订单的场景,避免回表);- 冗余索引:
order_status + create_time
(优化运营"按状态筛选订单"的统计需求)。
2. 冷数据存储:低成本归档集群
- 存储节点:迁移至MySQL冷备实例或Lindorm(时序数据库),采用HDD磁盘(成本降低60%),仅保留基础查询能力;
- 数据迁移策略 :
- 定时任务:每个季度末(如3月31日)自动将上上个季度的订单数据从热库迁移至冷库;
- 迁移方式:分批次迁移(每批次10万条),通过Sharding-JDBC的
DataPipeline
工具实现,避免锁表;- 查询路由:Sharding-JDBC拦截查询请求,若时间范围包含冷数据,自动路由到冷库查询,用户无感知。
3. 查询优化方案
- 用户查询 :仅路由到热库(最近3个月数据),通过
user_id + create_time
索引快速定位,响应时间控制在100ms内;- 运营统计 :
- 实时统计:基于热库数据,通过Sharding-JDBC的
SQL Federation
功能实现跨库聚合(如"今日订单总量");- 离线统计:冷数据+热数据通过Spark SQL定期计算(如"年度销售报表"),结果存储到ClickHouse,运营查询时直接查ClickHouse(响应时间<500ms)。
四、订单-库存强一致性保障
采用"TCC事务+最终一致性补偿"方案,确保订单创建与库存扣减的一致性:
1. 核心流程(TCC三阶段)
(1)Try阶段:资源预留
- 订单服务:创建"待支付"状态订单(
order_status=0
),写入分库分表,通过Sharding-JDBC确保订单落库原子性;- 库存服务:预扣减库存(
stock = stock - buy_num
,标记lock_stock = lock_stock + buy_num
),记录库存锁定日志(lock_log
,包含order_id
、product_id
、lock_num
);- 约束:Try阶段失败(如库存不足),直接返回用户"库存不足",订单不落地。
(2)Confirm阶段:确认执行
- 触发条件:用户支付成功(支付回调通知订单服务);
- 订单服务:更新订单状态为"已支付"(
order_status=1
);- 库存服务:确认扣减库存(
stock = stock - lock_num
,lock_stock = lock_stock - lock_num
),删除库存锁定日志;- 保障:Confirm阶段通过幂等设计(按
order_id
唯一标识),避免重复执行。
(3)Cancel阶段:回滚释放
- 触发条件:用户超时未支付(如30分钟)或支付失败;
- 订单服务:更新订单状态为"已取消"(
order_status=-1
);- 库存服务:释放锁定库存(
stock = stock + lock_num
,lock_stock = lock_stock - lock_num
),删除库存锁定日志;- 保障:Cancel阶段通过定时任务补偿(每5分钟扫描"待支付"且超时的订单,触发Cancel)。
2. 最终一致性补偿
- 定时对账:每小时对比"订单表"与"库存锁定日志表",若存在"待支付订单"但无对应库存锁定(或反之),触发补偿逻辑;
- 监控告警:若TCC任一阶段失败次数超过阈值(如10次/分钟),触发短信/钉钉告警,人工介入处理。
五、无感扩容方案
采用"一致性哈希+动态分片+灰度迁移"策略,实现分库分表的无感扩容(以"8库扩16库"为例):
1. 扩容前准备
- 新增分库 :部署8个新分库(
db_order_8
~db_order_15
),与原有分库配置一致(1主2从、SSD磁盘);- 配置中心 :在Nacos/Apollo中新增"16库分片规则"(
user_id % 16
),标记为"灰度状态"。
2. 灰度迁移数据
- 双写阶段 (1~2天):
- Sharding-JDBC开启双写模式:新订单同时写入"旧库(8库)"和"新库(16库)",读请求仍路由到旧库;
- 定时任务:迁移旧库中"最近3个月热数据"到新库(按用户ID哈希重新分片),每批次迁移10万条,迁移后校验数据一致性(MD5比对);
- 读切流阶段 (1天):
- 灰度路由:将10%的用户读请求路由到新库,监控响应时间、错误率(需<0.1%);
- 全量切流:若灰度无异常,逐步将100%读请求切换到新库,旧库保留写能力(避免双写中断)。
3. 扩容完成
- 停止双写:确认新库数据100%一致后,关闭旧库写能力,新订单仅写入16库;
- 旧库归档:旧库中3个月前的冷数据迁移至冷存储,旧库节点保留1周后下线(应对回滚);
- 监控保障:扩容后1周内重点监控分库QPS分布(确保16库均匀承载)、主从延迟(<100ms)。
六、性能压测与容量规划
1. 性能压测指标(大促峰值)
指标 | 目标值 | 压测结果 |
---|---|---|
写QPS(订单创建) | 10000 | 12000(达标) |
读QPS(用户查单) | 50000 | 65000(达标) |
订单创建响应时间 | P99 < 200ms | P99=150ms(达标) |
冷数据查询响应时间 | P99 < 1s | P99=800ms(达标) |
2. 容量规划(未来3年)
- 分库数量:当前8库→1年后16库→3年后32库(按年订单量10亿,32库×32表×100万行=10.24亿,预留20%冗余);
- 存储容量:热数据(3个月)约2.5亿行,按每行1KB计算,需250GB(8库×31GB/库,SSD磁盘);冷数据(3年)约30亿行,需300GB(HDD磁盘);
- 服务器配置:每分库主从节点采用4核8GB(写库)、8核16GB(读库),32库共需96台服务器(主32+从64)。
七、总结
本方案通过"时间+用户ID复合分片"解决数据规模问题,"Sharding-JDBC+读写分离"满足性能需求,"TCC+补偿"保障一致性,"一致性哈希扩容"实现无感升级,最终可支撑未来3年年均10亿订单的业务增长,同时兼顾用户查询体验与运营统计需求。