第 231 章:综合实战 - 电商系统设计
学习目标
- 设计电商系统全链路
- 综合应用前序章节知识
- 上下游协作流程
- 性能 + 一致性 + 容错平衡
一、业务全景
二、用户下单流程
2.1 完整时序图
2.2 状态机
三、关键设计点
3.1 库存:锁定 + 扣减
sql
-- 库存表
CREATE TABLE inventory (
sku_id VARCHAR(64) PRIMARY KEY,
total INT NOT NULL,
locked INT DEFAULT 0,
sold INT DEFAULT 0,
version INT DEFAULT 0
);
-- 锁定动作
UPDATE inventory SET locked = locked + ? WHERE sku_id = ? AND total - locked - sold >= ?;3.2 订单:不锁库存也能卖
设计:库存异步扣减,下单无锁,超卖由最终对账兜底。
3.3 支付:钱账一致
四步:
- 业务方预支付单
- 用户完成支付
- 收单方异步回调
- T+1 对账
3.4 营销:营销域隔离
优惠计算独立服务,不与订单强耦合。
四、数据模型
4.1 订单
sql
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
order_no VARCHAR(32) UNIQUE,
user_id BIGINT NOT NULL,
total_amount DECIMAL(10,2),
paid_amount DECIMAL(10,2),
status VARCHAR(20),
pay_id BIGINT,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE order_item (
id BIGINT PRIMARY KEY,
order_id BIGINT,
sku_id VARCHAR(64),
sku_name VARCHAR(255),
price DECIMAL(10,2),
quantity INT
);4.2 库存(乐观锁)
sql
CREATE TABLE inventory (
sku_id VARCHAR(64) PRIMARY KEY,
total INT,
locked INT DEFAULT 0,
sold INT DEFAULT 0,
version INT DEFAULT 0
);4.3 支付单
sql
CREATE TABLE payment (
id BIGINT PRIMARY KEY,
pay_no VARCHAR(32) UNIQUE,
order_id BIGINT,
amount DECIMAL(10,2),
pay_channel VARCHAR(20),
pay_status VARCHAR(20),
paid_at TIMESTAMP
);五、典型代码
5.1 订单聚合
java
public class Order {
private OrderId id;
private OrderNo orderNo;
private CustomerId customerId;
private List<OrderItem> items = new ArrayList<>();
private Money totalAmount;
private Money paidAmount;
private OrderStatus status;
public static Order create(CustomerId customerId, List<OrderItem> items) {
Order order = new Order();
order.id = OrderId.generate();
order.orderNo = OrderNo.generate();
order.customerId = customerId;
items.forEach(order::addItem);
return order;
}
public void confirm(Money total) {
if (items.isEmpty()) {
throw new IllegalStateException("订单必须包含商品");
}
this.totalAmount = total;
this.status = OrderStatus.PENDING;
registerEvent(new OrderConfirmedEvent(id, customerId, total));
}
public void markPaid(Money paid, PaymentId payId) {
if (!paid.equals(totalAmount)) {
throw new IllegalArgumentException("支付金额不匹配");
}
this.paidAmount = paid;
this.payId = payId;
this.status = OrderStatus.PAID;
registerEvent(new OrderPaidEvent(id, paid, payId));
}
public void cancel(String reason) {
if (status != OrderStatus.PENDING && status != OrderStatus.PAID) {
throw new IllegalStateException("当前状态不可取消");
}
this.status = OrderStatus.CANCELED;
registerEvent(new OrderCanceledEvent(id, reason));
}
}5.2 下单应用服务
java
@Service
@RequiredArgsConstructor
public class OrderAppService {
private final OrderRepository orderRepo;
private final PricingService pricingService;
@Transactional
public OrderId createOrder(CreateOrderCmd cmd) {
// 1. 创建订单
Order order = Order.create(cmd.customerId(), cmd.items());
// 2. 计算价格(含促销)
Money total = pricingService.calculate(cmd.customerId(), cmd.items());
order.confirm(total);
// 3. 保存
orderRepo.save(order);
return order.id();
}
}5.3 事件订阅示例
库存服务订阅订单事件:
java
@Component
public class OrderEventSubscriber {
@Autowired
private InventoryService inventoryService;
@KafkaListener(topics = "order-events", groupId = "inventory")
public void handle(DomainEvent event) {
if (event instanceof OrderConfirmedEvent e) {
inventoryService.lockStock(e.orderId(), e.items());
} else if (event instanceof OrderPaidEvent e) {
inventoryService.deductStock(e.orderId());
} else if (event instanceof OrderCanceledEvent e) {
inventoryService.unlockStock(e.orderId());
}
}
}六、系统非功能设计
6.1 高可用
| 维度 | 设计 |
|---|---|
| 入口 | SLB + 多可用区 |
| 网关 | 至少 2 实例 |
| 业务 | 微服务集群(3 实例起) |
| 数据库 | 主从 + 读写分离 |
| 缓存 | Redis Cluster |
6.2 大促保障
| 措施 | 说明 |
|---|---|
| 容量规划 | 预估峰值 8 倍冗余 |
| 数据库限流 | SQL 最大并发 |
| 流量整形 | Sentinel 限流 |
| 降级开关 | 关键路径降级预案 |
| 预案演练 | 模拟用户峰值压力测试 |
6.3 数据一致性保障
七、监控预警
7.1 关键指标
| 指标 | 阈值 |
|---|---|
| QPS | 5000 |
| P99 延迟 | < 500ms |
| 错误率 | < 0.1% |
| 库存差异 | = 0(对账) |
| 支付时效 | 30 秒内回调率 > 99.9% |
7.2 业务级监控
java
@Component
public class BusinessMetrics {
@Autowired
private MeterRegistry registry;
public void onOrderCreated(Order order) {
registry.counter("orders.created", "status", order.status().name())
.increment();
}
public void onOrderPaid(Order order) {
registry.counter("orders.paid", "amount", order.paidAmount().toString())
.increment();
}
}7.3 链路 + 日志
通过 traceId 串联:
八、容灾演练
8.1 故障注入
| 场景 | 验证 |
|---|---|
| Redis 挂 | 缓存击穿,数据库承压 |
| MQ 堆积 | 消费者追赶 |
| DB 主从切换 | 连接重连,数据连续 |
| 下游服务慢 | 熔断器降级 |
| 机房断网 | 流量切换 |
8.2 chaos engineering
bash
# ChaosBlade
blade create mysql full
blade create redis lose
blade create jvm OutOfMemoryError九、上线流程
yaml
# CI/CD 流水线
stages:
- test: mvn test
- build: docker build
- push: image to registry
- deploy: kubectl apply -k十、本章小结
| 主题 | 要点 |
|---|---|
| 业务建模 | DDD + 限界上下文 |
| 同步异步 | 关键路径同步 / 副作用异步 |
| 库存设计 | 锁定 + 扣减 / 超卖兜底 |
| 支付 | 状态机 + 幂等 + 对账 |
| 高可用 | 多活 + 容灾 + 降级 |
| 监控 | QPS + P99 + 业务指标 |
动手练习
设计一个简化的电商系统:
- 画出完整链路图
- 设计数据库表(订单、库存、支付)
- 用 Saga 描述下单完整流程
- 定义订单状态机
- 列关键告警规则
下一章:第 232 章:缓存体系实战