☁️博客首页:CSDN主页 搖了我吧的主页
📜 欢迎大家关注! ❤️
前言
所谓“超卖”指的就是商品卖多了,一般我们在商品扣减库存的时候,都会先判断库存够不够,如果够在进行扣减,不够则直接返回下单失败。但是,如果在高并发场景中,可能存在以下情况:
当有两个并发线程,同时查询库存,这时数据库中库存剩余1,所以两个线程都得到1的库存,然后经过库存校验之后分别开始进行库存扣减,最终导致库存被扣减成负数。
之所以会发生以上问题,主要是因为并发导致的,所以,解决超卖的问题本质上是解决并发问题。以上问题,最终就是要实现库存扣减过程中的原子性和有序性。
原子性:库存查询、库存判断以及库存扣减动作,作为一个原子操作,过程中不会被打断,也不会有其他线程执的。
有序性:多个并发操作需要排队执行。
商品一般分为普通商品和秒杀商品,这里结合业务采用不同的解决方案来防止库存超卖。
一、普通商品
普通商品的特点:
1、购买的时间相对分散
2、可以一次购买多个
根据普通商品的特点,我们可以借助数据库自己执行引擎的顺序执行机制,只要保证库存不要扣减成负数就行了,那么可以通过SQL语句就能控制:
update inventory
set quantity = quantity - #{count}
where sku id = '123' and quantity >= #{count}
也就是说,如果上述SQL可以执行成功的话,是可以确保库存余量大于等于0的,这就避免了超卖的发生。
看到这里,有些大牛会站出来说了,你这个方案不好。这种方案完全依赖数据库,并且高并发情况下,多个线程同时update inventory 的时候会发生阻塞,不仅会很慢,还会把数据库拖垮的。巴拉巴拉……
这里我想说,如果你普通订单的下单能把mysql搞跨,那应该不至于没钱升级数据库吧。其实想不依赖数据库也可以通过其他技术来解决超卖,但他们都缺乏可靠性,而且还会增加业务的复杂度。咱考虑业务的同时也要考虑实际情况,对不对?要是我的系统因为这条语句把数据库跑垮了我做梦都得笑醒~
二、秒杀商品
秒杀商品的特点:
1、购买的时间相对集中
2、正常一次只能购买一个
3、需要设置秒杀商品和库存数量
一般来说,秒杀商品是需要在后台添加的,所以我们要编写两个接口:
1、添加秒杀商品
/**
* 添加秒杀商品
* @author LiChangRui on 2024/3/15 13:59
*/
@Operation(summary = "添加秒杀商品")
@PostMapping("/createInstantGoods")
public Result<?> createInstantGoods() {
redisService.set("instantGoods:" + "这里是商品id", 这里是库存数量);
return Result.ok();
}
2、购买秒杀商品
/**
* 购买秒杀商品
* @author LiChangRui on 2024/4/25 13:38
*/
@Operation(summary = "购买秒杀商品")
@Parameters({
@Parameter(name = "goodsId", example = "1"
, description = "商品id", required = true, in = ParameterIn.QUERY)
})
@GetMapping("/buyInstantGoods")
public Result<?> buyInstantGoods(@RequestParam("goodsId") String goodsId) {
Long decr = redisService.decr("instantGoods:" + goodsId, 1);
if (decr < 0) {
redisService.incr("instantGoods:" + goodsId, 1);
System.out.println("库存不足");
return Result.error500("库存不足");
}
return Result.ok();
}
总结
如果您发现错误,还望及时提醒,共同进步。