侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1824 篇文章
  • 累计收到 0 条评论

设计电商系统的库存扣减服务

2025-12-12 / 0 评论 / 4 阅读

题目

设计电商系统的库存扣减服务

信息

  • 类型:问答
  • 难度:⭐⭐

考点

并发控制,分布式事务,缓存与数据库一致性,服务容错

快速回答

设计电商库存扣减系统的核心要点:

  • 并发控制:使用分布式锁(如Redis)或数据库乐观锁防止超卖
  • 扣减策略:采用预扣库存(冻结库存)模式,订单创建时预占,支付成功后实际扣减
  • 数据一致性:数据库与缓存通过双写+失效策略同步,使用事务消息保证最终一致性
  • 服务设计:独立库存服务,接口幂等,熔断降级机制
  • 扩展性:分库分表策略,商品库存按SKU分片
## 解析

1. 核心挑战与设计原则

核心问题:高并发场景下保证库存准确性,避免超卖(如秒杀场景10万QPS)

设计原则

  • 最终一致性优先于强一致性
  • 读多写少场景使用缓存优化
  • 接口必须幂等(相同请求重复处理结果一致)

2. 架构设计

┌─────────────┐       ┌─────────────┐       ┌───────────┐
│  订单服务    │───┬──▶│ 库存服务     │───┬──▶│ 数据库    │
└─────────────┘   │   └─────────────┘   │   └───────────┘
                  │                     │
                  │                     └──▶│ Redis缓存 │
                  │                         └───────────┘
                  │
                  └───▶ [MQ: 事务消息]

3. 关键实现方案

3.1 扣减流程(预扣库存模式)

  1. 用户下单:订单服务调用库存服务扣减接口(含SKU ID、数量)
  2. 预扣库存:库存服务执行
    UPDATE inventory 
    SET available = available - #{qty}, 
        locked = locked + #{qty}
    WHERE sku_id = #{skuId} AND available >= #{qty}
  3. 返回结果:成功则生成订单,失败提示库存不足
  4. 支付回调:支付成功后执行实际扣减
    UPDATE inventory SET locked = locked - #{qty} 
    WHERE sku_id = #{skuId}

3.2 并发控制方案

方案实现方式适用场景
乐观锁SQL中带版本号条件更新中等并发(1万QPS)
分布式锁Redis SETNX + Lua脚本超高并发(Redis集群支撑)
队列串行化RocketMQ顺序消息保证绝对顺序但延迟高

Redis分布式锁示例

// 使用Redisson实现
RLock lock = redisson.getLock("stock_lock:" + skuId);
try {
    if (lock.tryLock(100, 10, TimeUnit.MILLISECONDS)) {
        // 执行库存操作
    }
} finally {
    lock.unlock();
}

3.3 缓存与数据库一致性

双写+失效策略

  1. 更新数据库后删除Redis缓存
  2. 读取时若缓存不存在则回源数据库
  3. 使用canal监听binlog异步更新缓存

防击穿方案:缓存空值或使用Bloom过滤器

3.4 分布式事务保障

支付成功后的最终一致性

1. 订单服务提交本地事务(订单状态更新)
2. 发送事务消息到MQ(RocketMQ Half Message)
3. 库存服务消费消息执行实际扣减
4. 失败时重试+人工补偿

4. 容错与扩展设计

  • 熔断降级:Hystrix/Sentinel保护库存服务
  • 分库分表:按sku_id哈希分片(如1024个分片)
  • 热点库存:本地缓存+Redis分片前缀(stock:sku_{id})
  • 数据冗余:异步统计总库存到Elasticsearch供查询

5. 常见错误

  • 超卖风险:未在SQL中校验可用库存(WHERE available >= qty)
  • 缓存穿透:未处理不存在的SKU查询导致压垮数据库
  • 事务过大:扣减操作与业务逻辑放在同一事务导致锁竞争
  • 重试机制缺失:网络抖动导致扣减失败无自动补偿

6. 扩展知识

  • 库存分层:总库存 = 可用库存 + 锁定库存 + 预占库存
  • 秒杀优化:库存预热到Redis,扣减仅操作缓存异步落库
  • 动态扩容:Kubernetes HPA基于库存服务CPU自动扩缩容