侧边栏壁纸
博主头像
colo

欲买桂花同载酒

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

优化高并发下的商品库存扣减性能

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

题目

优化高并发下的商品库存扣减性能

信息

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

考点

数据库优化,缓存应用,锁机制选择

快速回答

优化高并发库存扣减的核心要点:

  • 使用Redis缓存库存数据减少数据库压力
  • 采用Lua脚本保证原子性操作
  • 数据库层使用乐观锁防止超卖
  • 引入队列削峰填谷处理并发请求
  • 设置库存缓存预热和同步机制
## 解析

问题场景

在电商秒杀场景中,当大量用户同时抢购某商品时,传统的直接操作数据库的方式会导致:

  • 数据库连接池耗尽
  • 行锁竞争导致死锁
  • 响应时间急剧上升
  • 库存超卖风险

优化方案

1. 缓存库存数据(Redis)

// 缓存预热(商品上线时)
$redis->set('stock:product_123', 1000);

原理: 将库存数据加载到Redis内存数据库,减少直接访问MySQL的次数。

2. 原子扣减(Lua脚本)

$lua = <<<LUA
local key = KEYS[1]
local change = tonumber(ARGV[1])
local current = tonumber(redis.call('GET', key))
if current >= change then
    return redis.call('DECRBY', key, change)
end
return -1
LUA;

$result = $redis->eval($lua, ['stock:product_123', 1], 1);

作用: 保证在分布式环境下的原子操作,避免超卖。

3. 数据库最终更新(乐观锁)

// 异步处理数据库更新
$queue->push(function () use ($productId) {
    DB::table('products')->where('id', $productId)
        ->where('version', $currentVersion) // 乐观锁
        ->decrement('stock', 1, ['version' => DB::raw('version + 1')]);
});

原理: 通过version字段实现乐观锁,避免更新冲突。

4. 消息队列削峰

// 请求入队(Controller层)
RabbitMQ::publish('stock_queue', json_encode([
    'user_id' => $userId,
    'product_id' => $productId
]));

作用: 将瞬时高并发请求转为顺序处理,保护后端系统。

最佳实践

  • 分层校验: 前端限流 → 缓存校验 → 队列缓冲 → 数据库持久化
  • 库存分段: 将总库存拆分为多个子库存段(如100件/段)分散压力
  • 缓存预热: 活动开始前预加载数据到Redis
  • 监控告警: 实时监控Redis内存/数据库负载

常见错误

  • ❌ 在PHP代码中做库存计算(非原子操作)
  • ❌ 使用SELECT FOR UPDATE导致数据库死锁
  • ❌ 缓存与数据库双写不一致
  • ❌ 未设置Redis操作重试机制

扩展知识

  • Redis持久化: 结合AOF和RDB保证数据安全
  • 限流策略: 使用令牌桶算法控制请求流量
  • 熔断机制: 当Redis不可用时降级到数据库层
  • 库存回滚: 订单取消时的库存恢复策略