面试-秒杀下单

部分摘自https://blog.csdn.net/u010391342/article/details/84372342

 

不加锁,肯定有问题:

 

用分布式锁如何解决库存超卖问题(有串行问题)

 

缺点:多个用户抢同一商品,需要串行,慢;

优化:分段

  • 相信很多人看过java里的ConcurrentHashMap的源码和底层原理,应该知道里面的核心思路,就是分段加锁!
  • 把数据分成很多个段,每个段是一个单独的锁,所以多个线程过来并发修改数据的时候,可以并发的修改不同段的数据。不至于说,同一时间只能有一个线程独占修改ConcurrentHashMap中的数据。
  • 另外,Java 8中新增了一个LongAdder类,也是针对Java
    7以前的AtomicLong进行的优化,解决的是CAS类操作在高并发场景下,使用乐观锁思路,会导致大量线程长时间重复循环。
  • LongAdder中也是采用了类似的分段CAS操作,失败则自动迁移到下一个分段进行CAS的思路。
  • 其实分布式锁的优化思路也是类似的,之前我们是在另外一个业务场景下落地了这个方案到生产中,不是在库存超卖问题里用的。

但是库存超卖这个业务场景不错,很容易理解,所以我们就用这个场景来说一下。大家看看下面的图:
在这里插入图片描述

  • 其实这就是分段加锁。你想,假如你现在iphone有1000个库存,那么你完全可以给拆成20个库存段,要是你愿意,可以在数据库的表里建20个库存字段,比如stock_01,stock_02,类似这样的,也可以在redis之类的地方放20个库存key。
  • 总之,就是把你的1000件库存给他拆开,每个库存段是50件库存,比如stock_01对应50件库存,stock_02对应50件库存。
  • 接着,每秒1000个请求过来了,好!此时其实可以是自己写一个简单的随机算法,每个请求都是随机在20个分段库存里,选择一个进行加锁。
  • bingo!这样就好了,同时可以有最多20个下单请求一起执行,每个下单请求锁了一个库存分段,然后在业务逻辑里面,就对数据库或者是Redis中的那个分段库存进行操作即可,包括查库存 -> 判断库存是否充足 -> 扣减库存。
  • 这相当于什么呢?相当于一个20毫秒,可以并发处理掉20个下单请求,那么1秒,也就可以依次处理掉20 * 50 = 1000个对iphone的下单请求了。
  • 一旦对某个数据做了分段处理之后,有一个坑大家一定要注意:就是如果某个下单请求,咔嚓加锁,然后发现这个分段库存里的库存不足了,此时咋办?
  • 这时你得自动释放锁,然后立马换下一个分段库存,再次尝试加锁后尝试处理。这个过程一定要实现。

 

实际上Redisson作为一款优秀的开源框架,我觉得他整体对分布式锁的实现是OK的

 

 

 

redis的队列主要思路:

内存处理秒杀类的商品抢购,至于数据库则是通过消息队列异步来更新

1、redis 中 缓存将要被秒杀的商品数量 对列明:sku:awards

2、因为 redis 是单线程的,所以可以将并发请求串行化,而且 Redis List 的 pop 操作是原子性的。

3、所有请求打到 Redis 上,都是从 sku:awards 队列上 pop 出一个元素:

A. 如果有,说明还有商品,那么需要把用户ID加入到Redis 的 Set 名为 candidate:userids 里:

​ a. 如果加入成功,说明用户是第一次抢购

​ b. 否则,说明用户已经成功抢购了,不能重复抢购,需要往队列 sku:awards 弥补一个商品标识元素

B. 没有取到,说明都被秒杀完了

伪代码如下:

public void testSecondKill(){
    Object goods = redisTemplate.opsForList().leftPop("sku:awards");
    if (goods != null){
        //模拟生成用户ID
        int num = new Random().nextInt(1000) + 1;
        Long result = redisTemplate.opsForSet().add("candidate:userids", num);
        if (result > 0){
            System.out.println("成功秒杀");
        } else {
            System.out.println("duplicate :::::: " + num);
 //商品数量恢复+1           redisTemplate.opsForList().rightPush("sku:awards", 1);
        }
    } else {
        //秒杀完毕
        System.out.println("秒杀失败");
    }
}



作者:逗逼程序员
链接:https://www.jianshu.com/p/7ebc8f751902
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

===========================非秒杀场景

库存加上>0约束

下单+减库存  使用事务,不成功就回滚;

之前写那个联合索引是干什么用的?忘了

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值