分布式订单系统,重点需要放在“高并发下单、分布式一致性、库存/支付/履约协同、可扩展性”这几块.
本篇文章结合电商场景来设计一个比较完整的分布式订单系统。
分布式订单系统设计
一、什么是分布式订单系统
分布式订单系统,本质上就是把传统单体里的“下单、库存、支付、优惠、物流、售后”等能力拆成多个独立服务,通过分布式架构来支撑高并发、高可用和高扩展。
在电商场景里,用户点一次“提交订单”,背后并不是一个简单的 insert,而是会串起很多动作:
- 校验商品是否可售
- 校验库存是否充足
- 计算价格、优惠、运费
- 锁定库存
- 生成订单
- 发起支付
- 支付成功后推进履约
- 超时未支付自动取消
- 取消后释放库存
所以,订单系统其实是整个交易中台的核心枢纽。
二、系统目标
设计分布式订单系统时,核心目标一般有这几个:
1. 高可用
订单是核心链路,不能轻易挂。即使某个非核心服务异常,也不能影响用户最基本的下单。
2. 高并发
大促、秒杀、直播带货时,下单流量会瞬时暴涨,系统要能扛住。
3. 数据一致性
订单、库存、支付状态必须尽可能一致,至少要保证最终一致性。
4. 可扩展
订单量越来越大后,必须支持分库分表、服务横向扩容、异步解耦。
5. 可追踪
订单生命周期长,必须能清晰看到每一步状态变化,方便排查问题。
三、典型业务场景
以一个电商平台为例:
用户购买一台手机,原价 3999,使用了 200 元优惠券,支付方式是支付宝。
整个订单链路可能是:
- 用户进入确认订单页
- 系统加载商品、地址、优惠、运费信息
- 用户点击提交订单
- 订单系统校验参数、价格、库存
- 库存系统锁库存
- 订单系统创建订单
- 支付系统生成支付单
- 用户完成支付
- 支付回调成功,订单状态改为“已支付”
- 仓储系统收到消息,开始发货
- 物流回传运单号
- 用户收货后订单完成
这条链路就是典型的分布式订单流程。
四、整体架构设计
可以把系统拆成下面几个核心服务:
1. 网关层
- API Gateway / Nginx
- 做统一鉴权、限流、路由、灰度、日志埋点
2. 接入层
- App、H5、PC、小程序
- 调用交易聚合服务或订单服务
3. 核心业务服务层
- 订单服务:订单创建、查询、状态流转、取消、关闭
- 购物车服务:维护用户待下单商品
- 商品服务:商品基本信息、上下架状态
- 价格服务:价格计算、优惠计算、促销规则
- 库存服务:库存扣减、锁定、释放
- 支付服务:支付单创建、支付状态同步
- 优惠券服务:优惠券校验、核销、回滚
- 履约/仓储服务:发货、出库、物流
- 用户服务:地址、会员等级、风控信息
- 售后服务:退款、退货、取消售后
4. 基础设施层
- MySQL / OceanBase:订单持久化
- Redis:缓存、幂等、分布式锁、热点数据
- RocketMQ / Kafka:异步解耦、最终一致性
- Elasticsearch:订单搜索
- XXL-JOB / Quartz:超时关单、补偿任务
- ShardingSphere:分库分表
- Prometheus + Grafana:监控告警
- SkyWalking / Zipkin:链路追踪
五、订单核心表设计
订单系统表设计一般会拆成几张主表,而不是一张大宽表。
1. 订单主表 order_info
核心字段通常有:
- order_id:订单号
- user_id:用户ID
- order_status:订单状态
- total_amount:订单总金额
- pay_amount:实付金额
- coupon_amount:优惠金额
- freight_amount:运费
- pay_status:支付状态
- source:订单来源(App/H5/小程序)
- create_time / update_time
2. 订单明细表 order_item
一笔订单可能有多个商品明细:
- id
- order_id
- sku_id
- spu_id
- sku_name
- buy_num
- sale_price
- item_amount
3. 订单地址表 order_address
保存收货信息快照,避免后续用户改地址影响历史订单。
4. 订单操作流水表 order_operate_log
记录状态流转:
- 订单创建
- 支付成功
- 发货
- 取消
- 完成
- 退款
5. 支付单表 pay_order
订单和支付往往分开存储,方便支持多支付渠道。
6. 库存锁定表 stock_reservation
记录这笔订单锁了哪些库存,便于超时释放和异常补偿。
六、订单状态机设计
分布式订单系统一定要有清晰的状态机。
例如:
INIT:初始化PENDING_PAYMENT:待支付PAID:已支付FULFILLING:履约中SHIPPED:已发货COMPLETED:已完成CANCELLED:已取消CLOSED:已关闭REFUNDING:退款中REFUNDED:已退款
支付状态、履约状态、售后状态最好拆开,不要全部塞进一个字段里,否则后面很难维护。
比如:
- 订单状态:待支付、已完成、已关闭
- 支付状态:未支付、支付中、已支付、已退款
- 发货状态:未发货、部分发货、已发货
这样更清晰。
七、核心下单流程设计
1. 确认订单页
用户点击“去结算”后,系统一般不会直接落库,而是先查:
- 购物车商品
- 实时价格
- 可用优惠券
- 收货地址
- 运费模板
- 库存是否可售
这一步更多是展示层聚合。
2. 提交订单
用户真正点“提交订单”时,核心流程一般是:
第一步:参数校验
- 用户是否合法
- 商品是否存在
- SKU 是否可售
- 收货地址是否有效
- 数量是否合法
- 是否重复提交
第二步:防重与幂等
通常要做两层:
- 前端防重复点击
- 服务端幂等控制
常见做法:
- 提交订单前生成
orderToken - Redis 保存 token
- 提交时校验并删除 token
- 删除成功才允许继续
这样可以防止用户连续点两次提交。
第三步:价格校验
不能信前端价格,必须服务端重算:
- 商品总价
- 促销优惠
- 优惠券抵扣
- 运费
- 实付金额
否则会被篡改。
第四步:锁库存
一般不建议直接扣减真实库存,而是先锁库存。
例如库存服务提供接口:
lockStock(orderId, skuId, num)releaseStock(orderId)deductStock(orderId)或支付成功后确认扣减
锁库存成功后,说明这笔订单有资格继续创建。
第五步:创建订单
订单服务落库:
- 订单主表
- 订单明细表
- 地址快照
- 订单日志
状态设为:待支付
第六步:发送下单成功消息
发送 MQ 消息给下游:
- 优惠券服务:冻结/核销优惠券
- 支付服务:生成支付单
- 风控系统:记录交易行为
- 营销系统:统计活动参与
第七步:返回下单结果
返回:
- 订单号
- 支付金额
- 支付链接或支付参数
八、库存与订单如何保证一致性
这是面试里最常问的点。
分布式系统里,订单和库存通常是两个独立服务、两套数据库,不可能靠本地事务一次性完成,所以通常采用最终一致性方案。
九、常见一致性方案
方案一:本地事务 + MQ 最终一致性
这是最常见、最实用的方案。
下单时
- 调库存服务锁库存
- 订单服务本地事务创建订单
- 本地事务成功后发送 MQ 消息
支付成功时
- 支付系统发送“支付成功”消息
- 订单服务更新为已支付
- 库存服务收到消息后正式扣减库存
- 履约系统收到消息后开始发货
超时取消时
- 定时任务扫描待支付超时订单
- 订单状态改为已取消
- 发送取消消息
- 库存服务释放锁定库存
- 优惠券服务回滚优惠券状态
这个方案的优点是:
- 性能高
- 解耦好
- 适合高并发
缺点是:
- 会有短暂不一致
- 需要补偿机制
方案二:TCC
适合资金、库存这类强业务约束场景。
比如下单:
- Try:尝试锁库存、预留资源
- Confirm:支付成功后确认扣减
- Cancel:失败或超时后释放
优点:
- 业务语义清晰
- 一致性更强
缺点:
- 侵入性高
- 开发复杂
- 对每个服务要求高
电商大部分订单主链路里,通常不会全链路都上 TCC,只会在关键链路局部使用。
方案三:Seata AT/XA
理论上可行,但在高并发交易系统里一般比较谨慎。
原因是:
- 性能损耗较大
- 长事务影响吞吐
- 对数据库和 SQL 有要求
- 容易成为瓶颈
所以电商订单系统更常见的还是 柔性事务 + 最终一致性。
十、如何防止超卖
这是订单系统和库存系统的结合点。
常见方案:
1. Redis 预扣减 + DB 最终落库
例如某商品库存 100:
- Redis 先放 100
- 请求到来先原子扣减 Redis
- 扣减成功再进入后续流程
- 最终异步同步到数据库
优点:快,适合秒杀
缺点:实现复杂,补偿要做好
2. 数据库乐观锁
SQL 类似:
update stock
set available = available - 1
where sku_id = ? and available >= 1 and version = ?
优点:简单
缺点:高并发下冲突严重
3. 库存分段/分桶
把库存拆成多个分片,降低热点竞争。
4. 队列削峰
用户请求先进入 MQ,消费者串行或有限并发处理某个商品的库存扣减。
十一、支付系统怎么衔接
订单系统与支付系统通常要解耦。
下单后
订单服务创建订单,状态为待支付,然后调用支付服务生成支付单。
支付成功后
支付平台回调支付服务,支付服务再做两件事:
- 更新支付单状态
- 发送“支付成功”消息给订单服务
订单服务消费消息后:
- 校验金额
- 校验订单状态
- 更新订单为已支付
- 记录支付流水
- 通知履约发货
这里一定要注意:
1. 支付回调幂等
支付平台可能多次通知,必须保证多次处理结果一样。
常见做法:
- 按支付流水号做唯一约束
- 按订单状态机控制重复更新
2. 金额校验
支付金额必须和订单应付金额一致。
3. 乱序处理
可能出现支付成功消息先到、订单状态稍后更新的情况,要有重试和补偿。
十二、订单号如何生成
分布式订单系统不能用数据库自增 ID 作为对外订单号。
常见方案:
1. 雪花算法 Snowflake
特点:
- 全局唯一
- 趋势递增
- 性能高
适合做内部主键 ID。
2. 业务订单号
一般会拼接:
- 日期
- 业务标识
- 用户尾号/机器号
- 随机数/序列号
例如:
OD202603261234560001
这样更适合展示给用户和客服。
十三、如何做幂等设计
订单系统里幂等非常重要。
1. 提交订单幂等
防止用户重复点提交:
- token 机制
- Redis setnx
- 请求号去重
2. 支付回调幂等
防止重复通知:
- 唯一流水号
- 状态机控制
3. MQ 消费幂等
防止消息重复消费:
- 业务去重表
- Redis 去重
- 唯一索引
例如支付成功消息消费时,可用 orderId + eventType 做唯一键。
十四、如何设计分库分表
订单数据增长很快,必须考虑分库分表。
1. 为什么要分库分表
因为订单表数据量很大,单表到几千万甚至上亿后:
- 查询性能下降
- 索引膨胀
- 写入压力大
- 备份恢复困难
2. 常见分片方式
按用户 ID 分片
优点:
- 查询用户订单很方便
缺点:
- 平台维度统计复杂
按订单号分片
优点:
- 分布均匀
缺点:
- 按用户查订单要带辅助索引
按时间分表
适合归档和冷热分离。
3. 实践中常见方案
通常组合使用:
- 库:按 user_id hash
- 表:按月份或 order_id hash
再配合 ShardingSphere 做路由。
十五、查询性能怎么优化
订单系统读请求很多,比如:
- 我的订单列表
- 订单详情
- 待付款订单
- 售后订单
- 商家发货列表
优化思路:
1. 索引设计
常见索引:
- user_id + create_time
- order_no
- status + create_time
- pay_status
2. 读写分离
主库写,从库读。
3. 缓存
热点订单详情可以放 Redis。
4. ES 检索
复杂条件组合查询、商家后台检索可接 Elasticsearch。
5. 冗余字段
在订单明细中冗余 sku_name、商品图片、下单价格,避免查历史订单时回源商品表。
十六、超时未支付怎么处理
这是订单系统标准能力。
典型做法:
方案一:延迟消息
订单创建后发一条延迟消息,比如 30 分钟后检查:
- 如果未支付,关闭订单
- 释放库存
- 回滚优惠券
方案二:定时任务扫描
定时任务扫描超时未支付订单。
大促时通常更推荐延迟消息 + 定时补偿结合。
因为:
- 延迟消息及时性更好
- 定时任务可兜底
十七、高并发场景如何设计
如果是普通电商订单,核心是稳定性。
如果是大促/秒杀订单,还要额外加强:
1. 请求限流
- 网关限流
- 用户维度限流
- SKU 维度限流
2. 削峰填谷
- MQ 异步排队
- 热点商品单独隔离
3. 热点隔离
热门商品不要跟普通商品共用完全相同的处理链路。
4. 降级
例如确认页优惠推荐、个性化标签这些非核心能力可降级,但“下单主链路”不能降。
十八、异常场景如何处理
面试里讲到这里会很加分。
1. 订单创建成功,库存锁定失败
直接回滚本地事务,返回下单失败。
2. 库存锁定成功,订单落库失败
发送补偿消息释放库存。
3. 订单已支付,但订单状态没更新
依赖支付消息重试 + 定时补偿任务。
4. 订单取消了,但库存没释放
库存释放消息重试,失败进入死信队列人工处理。
5. MQ 消息丢失
使用可靠消息机制:
- 生产者确认
- 消费者确认
- 消息持久化
- 本地消息表 / 事务消息
十九、一个推荐的面试落地回答
我会把订单系统拆成订单、库存、支付、商品、优惠券、履约等多个服务。
用户提交订单时,先做幂等校验和价格重算,再调用库存服务锁库存,成功后在订单服务本地事务里创建订单和订单明细,状态置为待支付。
下单成功后通过 MQ 异步通知支付服务生成支付单,同时发延迟消息处理超时关单。
支付成功后,支付服务发送支付成功消息,订单服务幂等消费后把订单改成已支付,再通知库存正式扣减、履约开始发货。
整个系统通过 Redis 做缓存和防重,通过 MQ 做异步解耦和最终一致性,通过分库分表支撑海量订单,通过状态机保证订单生命周期清晰可控。
一致性方面我倾向于使用本地事务 + MQ 最终一致性,而不是全链路强一致分布式事务,因为订单系统更看重吞吐、可用性和可扩展性。
二十、总结
一个好的分布式订单系统,核心不是“把订单表拆出来”这么简单,而是要解决四件事:
- 如何在高并发下稳定地下单
- 如何让订单、库存、支付在分布式下保持最终一致
- 如何通过状态机把订单生命周期管清楚
- 如何通过 MQ、缓存、分库分表让系统具备扩展性
把它理解成一句话:
订单系统是交易中台的核心编排系统,负责串联库存、支付、营销、履约等多个分布式服务,并通过状态机、幂等、消息最终一致性来保证交易链路稳定可靠。