美文网首页
秒杀系统设计思路

秒杀系统设计思路

作者: 无聊之园 | 来源:发表于2019-06-14 19:01 被阅读0次

秒杀场景:比如选课系统。当然并发不多,只是举个例子。
1、每个老师只有40个名额,但是有4000个学生并发来选某一个老师得课。

原则: 把请求拦截在系统上游。延长请求过程时间。

一、前端验证:
1、js限制每秒只能发送一个请求。
2、按钮或连接点击之后几秒不能再点击,
3、验证码。
4、答题。答题要生成题库,复杂了。
5、不跳转页面,直接发送json数据,ajax异步操作,省html资源。

二、站点层:
限制用户id请求次数,一秒内只能透过10个请求,防止别人for循环请求。
具体:随手写的代码,没有经过测试。
关于这里,可以看这里:(一)redis限流

RedissonClient redisson = Redisson.create();
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        String id = "123";
        RBucket<Boolean> bucket = redisson.getBucket("blackId:" + id);
        // 是否是黑名单得
        if(bucket.get() == true){
            // 或者返回最近的一个请求的缓存
            return;
        }
        RAtomicLong idAccessNum = redisson.getAtomicLong(id);
       while (true){
            long num = idAccessNum.get();
            // 没有初始化,则设置过期时间
            // 这种方式,并非准确10秒10次就到了阈值,可能前10秒已经有9次,然后后10秒又请求了9次,
            // 最长可能会有18次。但是我们不需要准确的10次,所以无影响。
            // 如果想准确的统计,可以使用zset
            if(num == 0){
                idAccessNum.expire(10, TimeUnit.SECONDS);
            }
            // 超过了10次,则进入黑名单
            if(num > 10){
                // 加入黑名单,30秒之后不能再访问
                bucket.set(true, 30, TimeUnit.SECONDS);
                // 或者返回最近的一个请求的缓存
                return;
            }
            // 这里自旋,只针对同一个用户id,因为理论上同一个用户的请求是线性的,所以这里自旋
            // 不会对性能造成很大的影响
            boolean updateSuccess = idAccessNum.compareAndSet(num, num + 1);
            // 是否更新成功
            if(updateSuccess){
                break;
            }
        }
        // 放行

三、服务层
站点层拦截了一部分之后,到了服务层的就是真实的有效的请求了。
直接用redis收集已经下单的数量,库存有多少,则放多少请求进来,其他,都返回失败。

真正进来的的请求之后, 如果压力还大,可以进入mq,但是一旦进入了mq, 则说明是异步的,前端需要轮询查询订单是否下单成功(这里不适合websocket,因为这个轮询不会很久,没必要弄一个websocket),或者,容错性比较大,个别错误了无所谓。

也可以,进入线程池,但是进入线程池,一旦,服务挂了,数据就丢失了。

  String userId = "123";
        String storeId = "345";
        // 用户是否已经下过订单
        RBucket<Long> isOrdered = redisson.getBucket(storeId + ":" + userId);
        if(isOrdered.get() == 0){
            // 查询数据库,然后更新缓存,然后判断是否已经下过订单
        } else if(isOrdered.get() == 2){
            //已经下过订单
            return;
        }
        // 获取库存id
        RBucket<Long> storeBucket = redisson.getBucket(storeId + ":num");
        Long storeNum = storeBucket.get();
        // 缓存没有,则去查数据库
        if(storeNum == 0){
            // 查数据库
            // 没有则去查数据库(如果并发太高,这里有缓存穿透),可以分布式锁控制
            // 可以更新库存的时候,直接写入缓存。
        }
        RBucket<Long> inNumBucket = redisson.getBucket("storeId:inNum");
        while (true){
            Long inNum = inNumBucket.get();
            // 订单已经满了,则返回,抢购失败
            if(inNum >= storeNum){
                return;
            }
            // 减少库存
            boolean isSuccess = inNumBucket.compareAndSet(inNum, inNum - 1);
            if(isSuccess){
                break;
            }
        }
        // 下单成功
        // 如果压力还大,可以进入mq,但是一旦进入了mq,
        // 则说明是异步的,前端需要轮询查询订单是否下单成功
        // 这里保证幂等性,因为,如果同一个用户,落到两台机器上,则可能都会进入这里
        // 比如,insert唯一键,失败了,则回滚,并且,回滚库存,加1

        

四、对于读请求
就加缓存,加机器,再不行就限流抛弃一部分请求。
站点层,可以cdn加速。

五、业务折衷
1、比如,先抢到一个许可证,之后用这个许可证去购买。
这样,在第一个抢票环节,就直接往mq种放,不需要考虑消息是否执行完成,马上返回抢票成功。
缺点:用户体验极差。真的有这种并发量,还不如允许mq异步,然后页面跳转然后轮询呢。
2、对于当前库存数量,不精确数字,比如12306的显示有票。
3、把抢购,分时段,比如12306的分批放票。

六、问题:
一、如果一个用户多个请求,落到不同的服务器上,将会造成,下多个订单的问题。
解决方案:
1、数据库做唯一键,保证幂等性。
数据库插入失败之后,回滚把库存加回去

2、如果是update操作,j则加where条件乐观锁机制,返回影响0条,则回滚库存。
3、或者,分布式锁,锁住这个用户商品抢购,然后去查询是否下单成功。
总之,保证幂等性。

相关文章

  • 秒杀系统设计思路

    昨天遇到这个问题,发现自己临时总结的不是很好,所以现在想重新整理一下思路。 分析一下问题: 类似淘宝那种做秒杀系统...

  • 秒杀系统设计思路

    秒杀场景:比如选课系统。当然并发不多,只是举个例子。1、每个老师只有40个名额,但是有4000个学生并发来选某一个...

  • 秒杀系统设计思路

    一、限流与降级 客户端限流 按钮置灰 js控制每秒只能发送一个请求 站点层限流 1. Nginx限流 Nginx官...

  • 秒杀系统设计总结(持续更新中。。)

    一、前言 秒杀系统其实是一个比较复杂的设计,文章先介绍设计秒杀系统的思路脉络和设计系统的原则。后面章节再详细介绍使...

  • 彻底弄懂 “秒杀系统”

    今天我们一起来看看,一套秒杀系统在架构设计上需要有哪些考量: 秒杀场景的特点 系统隔离的设计思路 客户端设计 代理...

  • 浅谈秒杀系统设计思路

    秒杀场景 例如双11、618 涉及问题 高并发:时间短,瞬间用户量 超卖:卖出量多于库存 数据库超负荷:数据库甚至...

  • 秒杀(二)

    背景:最近看自己以前写的博客。有一个有关秒杀的博客:秒杀系统设计思路[https://www.jianshu.co...

  • 用Redis轻松实现秒杀系统

    秒杀系统的架构设计 秒杀系统,是典型的短时大量突发访问类问题。对这类问题,有三种优化性能的思路: 写入内存而不是写...

  • 用Redis轻松实现秒杀系统

    秒杀系统的架构设计 秒杀系统,是典型的短时大量突发访问类问题。对这类问题,有三种优化性能的思路: 写入内存而不是写...

  • 用Redis轻松实现秒杀系统

    秒杀系统的架构设计 秒杀系统,是典型的短时大量突发访问类问题。对这类问题,有三种优化性能的思路: 1:写入内存而不...

网友评论

      本文标题:秒杀系统设计思路

      本文链接:https://www.haomeiwen.com/subject/meppfctx.html