秒杀场景思路设计
毕业之后我换了五六个手机号,可同学结婚还是联系到了我。
为什么总是喜欢秒杀场景呢,因为在电商平台就存在这种场景
秒杀系统场景特点
- 秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功
- 秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增
- 秒杀业务流程比较简单,一般就是下订单减库存
设计需要提前了解的词
限流: 鉴于只有少部分用户能够秒杀成功,所以要限制大部分流量,只允许少部分流量进入服务后端秒杀程序。
削峰:对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。实现削峰的常用的方法有前端添加一定难度的验证码后端利用缓存和消息中间件等技术。
异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。
内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。
可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了。像淘宝、京东等双十一活动时会增加大量机器应对交易高峰。
架构设计
设计思路
将请求拦截在系统上游,降低下游压力:秒杀系统特点是并发量极大,但实际秒杀成功的请求数量却很少,所以如果不在前端拦截很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时
充分利用缓存:利用缓存预减库存,拦截掉大部分请求。
消息队列:异步处理,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。
前端方案
页面静态化:将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。
禁止重复提交:用户提交之后按钮置灰,禁止重复提交。
用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流。
后端方案
限制uid(UserID)访问频率:我们上面拦截了浏览器访问的请求,但针对某些恶意攻击或其它插件,在服务端控制层需要针对同一个访问uid,限制访问频率。
服务层
1、把需要秒杀的商品的主要信息以及库存初始化到redis缓存中。
2、做请求合法性的校验(比如是否登录),如果请求非法,直接给前端返回错误码,进行相应的提示。
3、进行内存标识的判断(true 已经秒杀结束,false 未秒杀结束),如果内存标识为true,直接返回秒杀结束。
4、decr 进行预减库存操作,判断,如果decr后库存量小于0,则把内存标记置为true(已经秒杀结束),且返回秒杀结束。
5、判断是否已经秒杀到了,防止重复秒杀,如果重复秒杀,直接返回重复秒杀的错误码。
6、发送秒杀到的MQ消息给相应的业务端进行处理,并给用户端返回排队中,如果客户端收到排队中的消息,则自动进行轮询查询,直到返回秒杀成功或者秒杀失败为止。
7、相应的业务端进行处理:真正处理秒杀的业务端,再次进行校验(比如秒杀是否结束,库存是否充足等)、将用户和商品id作为key存入redis来标识该用户秒杀该商品成功(上述的第5步会用到)、减库存、生成秒杀订单、返回秒杀成功。
数据层
数据库层是最脆弱的一层,一般在应用设计时在上游就需要把请求拦截掉,数据库层只承担“能力范围内”的访问请求。所以,上面通过在服务层引入队列和缓存,让最底层的数据库高枕无忧
为防止秒杀出现负数订单数大于真正的库存数,所以在真正减库存,update库存的时候应该加上where 库存>0,而且需要给秒杀订单表加上用户id和商品id联合的唯一索引