Skip to content
第 231 / 250 章架构⏱ 18 分钟阅读

第 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 支付:钱账一致

四步:

  1. 业务方预支付单
  2. 用户完成支付
  3. 收单方异步回调
  4. 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 关键指标

指标阈值
QPS5000
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 + 业务指标

动手练习

设计一个简化的电商系统:

  1. 画出完整链路图
  2. 设计数据库表(订单、库存、支付)
  3. 用 Saga 描述下单完整流程
  4. 定义订单状态机
  5. 列关键告警规则

下一章:第 232 章:缓存体系实战

本站基于 VitePress 构建 · 由 Codebook 团队维护