高并发场景下秒杀券的库存超卖问题(乐观锁解决)

1. 秒杀券场景介绍

通过秒杀(Flash Sale)设计一种促销活动,特点是短时间内大量用户涌入系统,抢购有限的商品或优惠券。这种情况下的特点是:

  • 商品稀缺:库存量远小于需求量(如100张优惠券面对10万用户)。
  • 时间敏感:用户争抢的时间窗口极短,系统必须快速响应。
  • 高并发请求:在同一时间点,可能有成千上万的用户同时发起请求,系统需要处理大量并发请求。

2.库存超卖问题

在高并发场景下,如何保证库存扣减的正确性和数据一致性是一大难题。

库存超卖 是指在高并发场景下,系统未能正确处理多个用户同时扣减库存,导致库存被扣减到负数。例如:

  • 秒杀优惠券库存为1
  • 两个用户如AB等多个用户几乎同时提交抢购请求。
  • 系统中的业务逻辑如下:
    int stock = queryStock();  // 查询库存,返回1
    if (stock > 0) {           // 判断库存是否大于0
        updateStock(stock - 1); // 扣减库存,库存变为0
    }
    

    因此在执行 queryStock()updateStock() 之间,两个请求都读取到库存为1,判断通过后都执行扣减操作,导致库存最终变成 -1

3.导致库存超卖的核心原因

  • 请求并发导致的竞态条件(Race Condition)

    • 多个请求同时操作数据库,读取到相同的库存值,执行扣减操作时没有加锁或条件判断不够严格,导致超卖。
  • 查询与更新操作不具备原子性

    • 数据库操作分为两步:先查询库存,再更新库存。
    • 在高并发情况下,两个请求可以在查询与更新之间交替执行,导致数据不一致。
  • 数据库事务隔离级别不足

    • 数据库默认的隔离级别可能无法防止不可重复读,在秒杀场景下可能引发数据冲突。
  • 分布式系统中的延迟问题

    • 在缓存和数据库之间的数据同步存在延迟,导致不同节点读取到的库存数据不一致。

4.具体场景示例

假设有100张优惠券,面向10万用户进行抢购:

  1. 秒杀开始后,10万个请求瞬间到达服务器。
  2. 系统执行扣减库存逻辑:
    • 请求A请求B 同时读取到库存 stock = 1
    • 两个请求分别判断 stock > 0 为真。
    • 两个请求都执行 stock = stock - 1,库存最终变成 -1
  3. 数据库记录不一致,出现超卖现象

 5.乐观锁解决方案

乐观锁的核心思想是:在更新数据时检查数据是否被其他请求修改过,通过版本号或其他条件(如库存的值)进行判断,确保并发操作安全。

乐观锁实现步骤
  1. 读取数据时不加锁
  2. 更新时进行条件判断
    • 使用 where 条件判断数据是否满足期望值。
    • 如果条件不满足,说明数据已被其他请求修改,更新失败。

代码案例

一种乐观锁变体的方式来解决超卖问题:
boolean success = seckillVoucherService.update()
    .setSql("stock = stock - 1") // 扣减库存,stock = stock - 1
    .eq("voucher_id", voucherId) // 筛选指定的voucher_id
    .gt("stock", 0) // 确保库存 > 0
    .update(); // 执行更新操作

首先通过更新语句,将 stock 的值减1。通过 where stock > 0 条件,确保只有库存大于0时才会执行扣减操作。如果多个请求同时扣减库存,把当前的查询到的stock与数据库stock 进行对比,此时只有一个请求能满足 stock > 0 的条件,其他请求会因为条件不满足而失败。

乐观锁的优势

  • 避免超卖:通过 where stock > 0 的条件保证库存不会被减成负数。
  • 无需加锁:数据库级的条件判断实现了并发控制,不需要使用悲观锁(如 for update)。
  • 性能优越:相比于悲观锁,乐观锁更适合高并发场景,减少了锁带来的性能开销。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xwzzz_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值