设计一个秒杀系统-秒杀方案分析

学习使用,老鸟飞过,欢迎交流

秒杀系统应该考虑哪些因素

高可用:秒杀系统最大的特点就是并发高,在极短的时间内, 瞬间用户量大。试想一下双11的时候可能会有几十万的用户去访问同一个商品详情页面秒杀同一个商品,那么我们的系统如何能在这么大的并发请求下不被击垮?万一系统被击垮或者出了一些不可控的意外我们需要设置要一个兜底方案

高性能:一个秒杀系统还要考虑如何在大并发请求下拥有好的性能,让用户拥有更好的体验,如果用户需要等待十几秒甚至几十秒才能得到结果那这个体验是很差的。

一致性:最后还要考虑超卖问题,大并发请求下很容易出现商品库存的并发修改导致库存减到负数出现超卖现象,可以通过分布式锁来保证数据的一致性

1.前端优化

1.1.页面静态化

我们从前端说起,秒杀意味着大用户量和高并发请求,对于商品秒杀详情页来说页面的数据加载会给服务器带来很大的压力,可以把秒杀详情页静态化,反正这个页面的大部分数据都是固定不变的,然后把静态资源部署到Nginx实现动静分离

1.2.CDN加速

页面静态化+动静分离后,客户端直接从Nginx访问完整的静态页面,无需再发送Ajax请求从后端加载动态数据,减轻了服务器压力,提高了页面响应速度,这里可以进一步优化,就是将静态资源缓存起来,比如静态资源上CDN后,客户端可以从最近的CDN服务器节点获取静态资源,加快响应速度

1.3.前端限流

秒杀系统的并发非常的高,然后能够秒杀到商品的请求是很少的,大部分的请求都是无效的请求,我们需尽可能的减少没不要的流量,前端限流指的是从前端限制请求的并发量,比如未到秒杀时间禁用秒杀按钮,点击了秒杀按钮后马上禁用按钮,需要等n秒才能点击第二次等来减少用户的请求数量,或者增加图片验证码或答题等手段。

1.4.流量错峰

流量错峰就是尽可能的将流量分散到更大范围的时间点,比如秒杀时加入图片验证码,或者需要答题,每个人输入图片验证码的时间都不一样,相当于就是把流量分散了

2.后端优化

2.1.链接加密

秒杀地址不能提前暴露,因为有被提前秒杀的风险,即使秒杀逻辑做了秒杀时间判断,如果秒杀地址被暴露,使用程序不停获取最新时间不停刷秒杀接口,那被秒杀到的机率是非常高的,可能所有的商品都被程序秒杀到。那如何实现连接不被暴露呢?可以把URL动态化,使用MD5加密,通过前端获取URL,后端进行校验。

2.2.恶意拦截

对于恶意的请求要做拦截处理,否则会给系统带来没必要的流量,这种恶意请求一般是通过程序或脚本不停的发请求,这种情况请求会消耗带宽不说还可能造成缓存击穿,甚至把服务器打崩。这个可以在Nginx层实现。

2.3.后端限流

前端限流能够减少很少一部分并发,大量的请求还是会打到后端服务器,如果我们的服务器无法应对这么高的并发可能会被流量击垮,保守起见我们可以通过后端限流手段把多余的流量排除在外,那么实实在在参与秒杀的流量又少了很大一部分,限流的组件常用的有Sentinel、Hystrix等

2.4.服务隔离

为了让秒杀有更充足的资源不和别的服务抢占资源,需要将秒杀系统独立出来,这里分为:业务独立,服务独立,数据独立,独立的秒杀微服务和独立的数据库,同时秒杀服务也应该具有伸缩性,可集群,这样的系统才能支持更高的并发

2.5.库存预热

秒杀设计到库存判断和减库存,通常有“付款减库存”,“下单减库存”和“预减库存”等方式,对于秒杀而言,如果每次秒杀都需要先去数据库检查库存,然后再扣减库存,然后再保存订单数据,那在高并发请求下数据库是吃不消的,性能也会特别差,所以这里可以使用“预减库存”方案。

非关系学数据库的出现就是为了解决传统关系型数据库在性能上的不足,我们可以把商品库存事先存储到Redis中,每次秒杀商品时去Redis检查库存,同时基于Redis扣减库存,事后可以异步更新到数据库,这里Redis就解决了数据库性能问题,让秒杀流程变得更高效,但是如果有比较复杂的减库存逻辑,或者需要使用事务,你还是必须在数据库中完成减库存

这里还有一个问题,如果是一般的秒杀并发没有太高的话,一个Redis即可,几万的并发还是扛得住,但是如果要支持更高的并发量,那可能一个Redis就吃不消了,可以考虑Redis集群

2.6.数据缓存

把秒杀的商品相关数据缓存起来,减少和数据库的交流提高响应速度,最好的方案就是把秒杀商品缓存到Redis中,秒杀逻辑从Redis中进行读写。

2.7.异步消峰

下单是一个很耗时的工作,除了设计到大量的业务外还要和数据库进行交流,秒杀系统要的就是快,如果一个秒杀请求进来秒杀到商品后需要等待下单流程执行完成之后才返回结果,那这个请求是很耗时的,在高并发请求下系统的吞吐量就会很低,为了提高请求响应速度,可以采用异步下单,当秒杀服务秒杀到商品后,直接往MQ扔一个下单消息后就可以返回消息给用户,提示秒杀成功,而订单服务监听下单消息,获取消息后就执行下单逻辑即可,这样一来秒杀逻辑是非常快速的,因为采用异步下单,秒杀服务不用等待下单完成即可返回结果,同时也起到了一个消除峰值的效果,因为下单消息都在MQ排队呢

2.8.熔断降级

熔断降级是必须的,如果系统真的顶不住了,不做熔断降级措施可能整个系统都会瘫痪,做了降级至少对故障服务隔离掉,返回兜底数据,不至于服务器直接瘫痪全是500,404什么的。

2.9.超卖问题

这是一个比较重要的话题,在高并发请求下很容易出现超卖,比如三个秒杀服务都判断到还剩1一个库存,三个秒杀服务都去扣减库存,那库存就会被减成-2,这就出现了超卖了。这个问题可以使用分布式锁来解决,让多个服务对库存的扣减操作成原子性。可以使用Redis的信号量实现

2.10.支付超时

秒杀到商品后即可生成订单号,然后使用该订单号下单,下单采用MQ异步下单,秒杀服务往MQ扔一个小单消息,订单服务作为消费者从MQ取消息然后进行下单。秒杀成功秒杀服务就可以返回,用户即可根据订单号完成支付,支付成功后修改订单状态。那如果用户秒杀到商品后迟迟未支付呢?我们是不是需要为支付超时的订单退回库存呢?

这里可以使用延迟队列实现,下单成功后放入一个订单消息到延迟队列A,达到超时时间的消息成为死信消息,消息会通过一个死信交换机转发到另外一个队列B,我们可以指定一个消费者从该队列B中消费消息,判断支付状态如果支付超时,就做退库存操作。

2.11.网络开销

如果一个请求经过的服务太多注定比较耗时,每一次服务的调用都是网络开销,所以尽可能的减少服务的调用,在上面的秒杀案例流程图中其实网关层可以去掉请求直接使用Nginx负载到秒杀服务,减少网络开销,或者也可以请求直接走网关不走Nginx来减少网络开销,同时数据要少,网络传输尽可能少的数据来提高网络传输速度(select * 的不要),甚至可以使用压缩传输,当然压缩又会增加CPU的消耗。

设计一个秒杀系统,方案如下:
设计一个秒杀系统-秒杀方案分析_第1张图片

文章结束,希望对你有所帮助

你可能感兴趣的:(《微服务项目相关》,秒杀)