高并发场景下做库存扣减,如何避免超卖和少卖?

199 阅读3分钟

高并发场景下做库存扣减,如何避免超卖和少卖?

回答

一个典型的高并发场景下的超卖问题。

image-20250401180630450

  1. 问题本质

    超卖问题是由并发导致,

    解决该问题本质是处理并发问题

    解决的关键在于实现库存扣减过程中的原子性和有序性

  2. 原子性定义

    库存查询、判断及扣减这一系列操作构成一个不可分割的原子操作,

    在执行过程中不会被中断,也不允许其他线程同时执行,

    以此保证操作的完整性和一致性。

  3. 有序性定义

    面对多个并发操作,需要让它们按照一定顺序排队执行,

    避免并发冲突引发超卖等问题 。

1、数据库 扣减
2、Redis 扣减
  • 实现原理

    利用 Redis 单线程执行特性 以及 Lua 脚本执行过程的原子性保障。

    先从 Redis 取出当前剩余库存,判断是否足够扣减,

    若足够,则执行扣减操作,否则返回库存不足。

    由于 Lua 脚本执行时不会被打断(原子性保障),且 Redis 执行是单线程的。

    所以,在脚本中,先判断,再扣减的过程能避免并发问题,实现库存扣减的原子性和有序性。

  • 方案优势

    Redis 是高性能的分布式缓存,基于 Lua 脚本的扣减库存方案具有较高的执行效率。

  • Lua 脚本

image-20250222232023868

3、一致性保证
  • 一般,在实际应用过程中,数据库扣减 和 Redis 扣减会结合使用,进行库存扣减。

    1. 先在Redis中做扣减,利用Redis来抗高并发流量。
    2. 然后,再同步到数据库中做扣减,进行持久化存储。
  • 具体实现步骤

    1. 先在 Redis 中,做库存扣减。
    2. 接着发送 MQ 消息。
    3. 消费者接收到消息后,在数据库中进行库存扣减和业务逻辑操作。
    4. 这样,我们可以保证Redis和数据库的最终一致性。

image-20250401195339605

  • 问题:

    1. 这个方案可能导致少卖。
4、少卖
  • 原因分析

    Redis 中库存成功扣减,但 MQ 消息未发出,或在消息消费过程中丢失、失败等。

    最后,导致数据库库存未扣减,业务未实际操作,造成 Redis 多扣库存。

  • 解决思路:引入对账机制

    如:使用 zset 在 Redis 中添加流水记录,定时与数据库记录对比。

    发现不一致时,及时补偿处理。

    成熟电商公司通常都有此类核对系统,用于及时发现超卖、少卖问题。

5、为什么不用分布式锁?
  • 上面这个方案,为什么选择用 lua 脚本实现库存扣减?为啥不用Redis 的分布式锁呢
  • 喜欢的话可以 + 关,持续更新中……