高并发下订单库存防止超卖策略

打个广告

我的架构设计专栏:https://www.nowcoder.com/creation/manager/columnDetail/0ybvLm

我的java八股专栏:https://www.nowcoder.com/creation/manager/columnDetail/j8ZZk0

八股专栏内有详细苍穹外卖话术哦!

欢迎大家订阅!

0.什么是超卖问题?

在并发的场景下,比如商城售卖商品中,一件商品的销售数量>库存数量的问题,称为超卖问题。主要原因是在并发场景下,请求几乎同时到达,对库存资源进行竞争,由于没有适当的并发控制策略导致的错误。

例如简单的下单操作,通常我们会按照如下写法

public ServerResponse createOrder(Integer userId, Integer shippingId){
    // 执行查询sql select amount form store where postID = 12345;
    // 判断是否大于0然后执行更新操作 update store set amount = amount - quantity where postID = 12345;
}

由于如上的写法在应用层没有任何并发控制,如果 postID 为12345的商品库存为1件,此时有两个请求到达,先后执行了查询sql,则通过MySQL读取库存时,会加共享锁,因此都能获取到商品库存为1件,然后又分别执行更新操作,MySQL会将两个更新操作串行化执行,依次成功减库存,因此库存数量变成-1。

1.悲观锁(并发很高的场景不适用)

悲观锁主要用于保护数据的完整性。当多个事务并发执行时,某个事务对数据应用加锁,则其他事务只能等该事务执行完了,才能进行对该数据进行修改操作。

update goods set num = num - 1 WHERE id = 1001 and num > 0

假设现在商品只剩下一件了,此时数据库中 num = 1;

但有 100 个线程同时读取到了这个 num = 1,所以 100 个线程都开始减库存了。

但你最终会发觉,其实只有一个线程减库存成功,其他 99 个线程全部失败。update操作会自动加排它锁

需要注意的是,FOR UPDATE 生效需要同时满足两个条件时才生效:

  • 数据库的引擎为 innoDB
  • 操作位于事务块中(BEGIN/COMMIT)

悲观锁采用的是「先获取锁再访问」的策略,来保障数据的安全。但是加锁策略,依赖数据库实现,会增加数据库的负担,对于并发很高的场景并不会使用悲观锁,会导致其他事务都会发生阻塞,造成大量的事务发生积压拖垮整个系统。

2.乐观锁

在商品表中增加一个版本号字段,在进行更新操作之前会去比较此时的版本号和刚开始操作的时候的版本号是不是一致,如果是一致的那么就可以让这个更新操作正常执行,版本号+1。如果此时的版本号不一致了,那么就无法进行更新操作了。

select version from goods WHERE id= 1001

update goods set num = num - 1, version = version + 1 WHERE id= 1001 AND num > 0 AND version = @version(上面查到的version);

假设此时 version = 100, num = 1; 100 个线程进入到了这里,同时他们 select 出来版本号都是 version = 100。

然后直接 update 的时候,只有其中一个先 update 了,同时更新了版本号。

那么其他 99 个在更新的时候,会发觉 version 并不等于上次 select 的 version,就说明 version 被其他线程修改过了。那么我就放弃这次 update。

使用乐观锁需修改数据库的事务隔离级别:

使用乐观锁的时候,如果一个事务修改了库存并提交了事务,那其他的事务应该可以读取到修改后的数据值,所以不能使用可重复读的隔离级别,应该修改为读取已提交(Read committed)

缺点:虽然防止了超卖,但是会导致很多线程更新库存失败了,所以在我们这种设计下,1000个人真要是同时发起购买,可能只有100个幸运儿能够买到东西。

3.Redis原子操作(Redis incr)+乐观锁+lua脚本(推荐)

利用Redis increment 的原子操作,保证库存数安全

  1. 先查询redis缓存中是否有库存信息这样就可以减少访问数据库的次数。 获取到后把数值填入redis,以商品id为key,数量为value。 还需要设置redis对应这个key的超时时间,以防所有商品库存数据都在redis中。

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java抽象带蓝子的笔记专栏 文章被收录于专栏

我的笔记专栏,内有自己整理的八股知识笔记和算法刷题笔记,我会不断通过他人和自己的面经来更新和完善自己的八股笔记。专栏每增加一篇文章费用就会上涨一点,如果你喜欢的话建议你尽早订阅。内有超详细苍穹外卖话术!后续还会更新其他项目和我的实习经历的话术!敬请期待!

全部评论
..
点赞 回复 分享
发布于 07-29 21:34 广东

相关推荐

7 13 评论
分享
牛客网
牛客企业服务