如何根据并发量选择合适的库存方案?

在评估并发量并选择合适的库存控制方案时,通常可以从QPS(每秒查询次数)、TPS(每秒事务数)、库存竞争程度等多个维度进行衡量。以下是详细的评估方式和推荐的实现方案:


1. 并发量的核心指标

在选择具体的实现方案前,需要先衡量并发量的大小,主要关注以下指标:

📌 1.1 QPS(Queries Per Second,每秒查询次数)

  • 计算方式: QPS=总查询次数总时间(秒)QPS = \frac{\text{总查询次数}}{\text{总时间(秒)}}
  • 作用:
    • 衡量系统的读压力,如果库存查询操作频繁,则可能需要优化读性能(如缓存)。
    • 如果 QPS 高,数据库可能需要读写分离

📌 1.2 TPS(Transactions Per Second,每秒事务数)

  • 计算方式: TPS=总事务数总时间(秒)TPS = \frac{\text{总事务数}}{\text{总时间(秒)}}
  • 作用:
    • 衡量系统的写压力,如果 TPS 高,数据库的锁冲突可能严重
    • 如果 TPS 过高,可以考虑使用Redis 预扣库存等方案来缓解数据库压力。

📌 1.3 并发用户数(Concurrent Users)

  • 计算方式: 并发用户数=活跃用户数×请求频率系统响应时间\text{并发用户数} = \frac{\text{活跃用户数} \times \text{请求频率}}{\text{系统响应时间}}
  • 作用:
    • 衡量有多少用户同时下单,如果并发用户多,需要优化锁机制,避免长时间阻塞。

📌 1.4 订单库存竞争率

  • 定义:有多少用户在抢购同一个商品。
  • 计算方式: 库存竞争率=针对同一商品的请求数总请求数\text{库存竞争率} = \frac{\text{针对同一商品的请求数}}{\text{总请求数}}
  • 库存竞争率高(>50%):比如秒杀活动,适合Redis 预扣库存等高性能方案。
  • 库存竞争率低(<10%):适合数据库锁(乐观锁或悲观锁)方案。

2. 根据并发量选择合适的方案

并发量 QPS TPS 并发用户 适用方案 说明
低并发 <100 <50 <10 SELECT ... FOR UPDATE 适合小型业务,数据一致性要求高,锁竞争低
中等并发 100~1000 50~300 10~100 ✅ 乐观锁(version)✅ Redis 分布式锁 适合普通电商场景,减少数据库锁竞争
高并发 1000~10,000 300~5000 100~1000 ✅ Redis 预扣库存 ✅ 订单队列异步处理 适合大促销、抢购,避免数据库瓶颈
超高并发 >10,000 >5000 >1000 ✅ 限流+异步扣减 ✅ CDN 缓存 适合秒杀,数据库无法承受高并发写入

3. 各种库存控制方案的优缺点

方案 适用场景 优势 缺点
悲观锁(SELECT ... FOR UPDATE 低并发,数据强一致性场景 数据强一致性 低效,容易死锁
乐观锁(version 字段) 中等并发,减少锁冲突 高并发下比悲观锁好 并发极高时失败率上升
Redis 分布式锁 分布式系统,库存竞争不激烈 支持跨节点,减少数据库压力 需要额外 Redis 依赖
Redis 预扣库存 秒杀、高并发场景 高性能,不依赖数据库锁 需要定期回滚库存,数据一致性较低
消息队列异步扣减 超高并发(如12306抢票) 降低数据库压力,支持峰值削峰 复杂度高,数据最终一致性

4. 具体实现方式及用户可能遇到的问题

🔹 方案 1:数据库悲观锁

1
2
3
4
START TRANSACTION;
SELECT stock FROM stock WHERE product_id = 1 FOR UPDATE;
UPDATE stock SET stock = stock - 1 WHERE product_id = 1;
COMMIT;

用户体验

  • 能保证库存正确,用户不会超卖。 🚫 可能遇到的问题
  • 如果高并发,用户可能因为锁等待而超时。
  • 可能出现死锁。

🔹 方案 2:数据库乐观锁

1
2
UPDATE stock SET stock = stock - 1, version = version + 1 
WHERE product_id = 1 AND version = ?;

用户体验

  • 失败后用户可以重新尝试。 🚫 可能遇到的问题
  • 并发过高时,失败率可能上升。

🔹 方案 3:Redis 分布式锁

1
2
3
4
5
6
7
RLock lock = redissonClient.getLock("lock:stock:" + productId);
try {
lock.lock(10, TimeUnit.SECONDS);
// 查询库存、扣减
} finally {
lock.unlock();
}

用户体验

  • 访问速度快,响应快。 🚫 可能遇到的问题
  • 需要考虑 Redis 持久化,否则重启时可能丢失数据。

🔹 方案 4:Redis 预扣库存

1
DECR stock:product:1

用户体验

  • 下单速度快,基本不会超时。 🚫 可能遇到的问题
  • 需要定期校正库存,否则可能产生幽灵库存(比如支付失败但库存已扣减)。

🔹 方案 5:消息队列削峰

用户请求 → 先写入 MQ → 后端消费 MQ 扣减库存用户体验

  • 即使高并发,用户不会因为锁竞争卡住。 🚫 可能遇到的问题
  • 订单处理不是实时的,需要异步确认库存。

5. 如何评估你的业务适用哪种方案?

  1. 先评估 QPS、TPS、库存竞争程度
    • 低并发(QPS < 100):可以用数据库锁。
    • 高并发(QPS > 1000):需要 Redis 或消息队列方案。
  2. 考虑数据一致性要求
    • 必须强一致性 → 使用数据库事务或 Redis 分布式锁。
    • 允许短时间数据偏差 → 用 Redis 预扣库存。
  3. 评估业务场景
    • 普通订单:用乐观锁数据库行锁
    • 秒杀抢购:用Redis 预扣库存 + 异步处理

6. 总结

  • 并发量小(QPS < 100):数据库锁足够。
  • 并发量中等(QPS 100~1000):乐观锁 + Redis 分布式锁
  • 高并发(QPS > 1000)Redis 预扣库存 + 消息队列异步扣减
  • 超高并发(QPS > 10,000)限流 + CDN + 订单削峰

你目前的业务大概是什么量级?可以帮你分析适合的方案 😊