设计一个简易秒杀系统思路

很多的电商平台,在节假日如双十一,618等都会有商品描述的活动,今天和大家讨论一下,如何设计一个秒杀系统。

什么是秒杀
在一定的时间内几秒或者几分钟,对一定数量的库存进行出卖。

场景分析:

1,在秒杀之前,比如上午十点开始秒杀,很多用户可能在九点五十左右就开始访问自己心仪的秒杀商品,这样就会出现在秒杀之前用很多的并发量,所以在秒杀之前的半个小时之前,是否可以将参加秒杀活动商品的一些信息缓存起来,这样就可以减小因为高并发访问,给数据库带来的压力。

2,有的用户为了抢到商品可能利用第三插件,去频繁的访问接口,这样给接口会带来很大的压力,为了避免这种刻意的刷单问题,可以在后台对同一个用户的访问频率做限制,可以预测接口的实际访问频率,然后对不同的接口,同一个用户做频率限制,如某个抢购入口,可以设置一个用户一分钟不能访问超过60次,10秒内不能超过20次等不同时段不同访问频率策略,这个可以通过redis等缓存框架做到,列入以某个接口+用户唯一性标准为key,可以选择value为string类型,如果缓存更多数据,value可以选择hash类型,value的值是int类型,这样用户每次访问接口,可以先判断该值有没有达到预设的访问频率限制的值,如果达到了,就告诉用户,你的访问太过频繁,请多长时间后再试,或者要求用户输入验证码(图片验证码或者短信验证码)。如果没有达到预设值,就给int值技术加一,在第一次访问的时候,即查询的前exist判断,如果不存在,插入第一条数据,并且设置过气时间,这个时间就是访问频率的时间限制例如一分钟只能访问60次,那么这里的过期时间就是60秒,预设值就是60次。这样就可以从一定程度上避免刷单问题。

3,一个用户可能当初注册了很多的账号,平时不用,专门用来参加秒杀活动,这样其实也会造成系统压力。解决方式和上述差不多,只是对ip做限制,但是怎样做可能伤到真实的用户,因为有的场合如网吧,里面人购物其实是同一个ip出口,这个时候,其实也可以对用户的等级做限制,只有多高的等级才可以参加秒杀活动。

秒杀场景的特点

  • 同一时间同时进行抢购,网站瞬时访问流量激增。
  • 访问请求数量远远大于库存数量,但是只有少部分用户能够秒杀成功。
  • 秒杀业务流程比较简单,一般就是下订单减库存

秒杀设计思路

前端页面静态化,禁止重复提交

  • 页面静态化:将前端可以静态的资源静态化。

  • 禁止重复提交:秒杀开始之后,可以对用户点击后响应前按钮置灰。

后端可拓展,缓存,限流,削峰,异步处理

  • 可拓展:服务的可扩展,可以水平添加机器将用户请求分担到不同的机器上去。数据库可扩展,支持分库分表,对于用户的请求,映射到不同的数据库,减少单台数据库的压力。

  • 内存缓存:参加秒杀系统的商品是事先可知的,可以将参加秒杀的商品信息事先缓存到redis等缓存系统中,这样可以大大的提高系统的吞吐量,减少关系型数据库的读写压力。

  • 限流: 一单秒杀开始,实际秒杀成功的用户只是库存的数量,在库存没有之后,将前端的秒杀入口关闭。

  • 削峰:数据库削峰。对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。对于关系型数据库而言,这个是致命的,是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。实现削峰的常用的方法有利用缓存和消息中间件等技术。

  • 异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。

数据库层特点

数据库层是最脆弱的一层,一般在应用设计时在上游就需要把请求拦截掉,数据库层只承担“能力范围内”的访问请求。所以,上面通过在服务层引入队列和缓存,让最底层的数据库高枕无忧。

案例:利用redis+mysql实现简单的秒杀系统

Redis是一个分布式key-value缓存系统,value支持多种数据结构,这里value可以选择两种类型,String(或者hash):主要用于记录商品的库存,对商品减库存。Set集合(这里不要用list集合,list集合是可重复的,set是不可重复的,可以保证一个用户只卖一次,如果一个用户可以买多次那么可以使用list集合):用于存储用户的id获取其他唯一确定一个用户的值。

在秒杀开始的前:可以使用批处理,将参加秒杀的产品信息缓存到redis中。这里将产品的业务唯一字段作为key,库存作为value。这里的key要和前端缓存的key一致。
在秒杀开始时::用户大量提交。根据用户提交的产品信息,获取到redis中需要的key值,查询缓存(为了保证缓存有效,如果第一次没有查询到,可以到数据库查询,然后在缓存一下,不过一般不会出现),得到库存量,判断当前库存是否大于零,如果大于零,判断当前的set集合中是否用该用户ID,如果没有,减库存并且将用户的ID放入集合中,并对库存减一,如果库存为0,提示用户,商品已售完等文案信息,如果集合中已经存在该用户id,则不做任何处理,直接处理下一个请求。直到最后库存售完,上面的过程可以利用redis事务和watch功能完成对数据一致性的控制即超卖问题
库存售完后:程序开始启动一个有个后台线程,可以阻塞等待商品库存售完的通知,在上面一步,库存一旦售完,后台进程获取set集合中的用户信息,异步处理需要操作的购买等后续操作。

这只是一个简单的秒杀系统,实际的秒杀系统,还要考虑到很多的真实场景需要很多完善的地方,希望大家一起讨论如何设计一个秒杀系统。

秒杀系统的难点

首先我们先看下秒杀场景的难点到底在哪?在秒杀场景中最大的问题在于容易产生大并发请求、产生超卖现象和性能问题,下面我们分别分析下下面这三个问题:

1)瞬时大并发:一提到秒杀系统给人最深刻的印象是超大的瞬时并发,这时你可以联想到小米手机的抢购场景,在小米手机抢购的场景一般都会有10w+的用户同时访问一个商品页面去抢购手机,这就是一个典型的瞬时大并发,如果系统没有经过限流或者熔断处理,那么系统瞬间就会崩掉,就好像被DDos攻击一样;

2)超卖:秒杀除了大并发这样的难点,还有一个所有电商都会遇到的痛,那就是超卖,电商搞大促最怕什么?最怕的就是超卖,产生超卖了以后会影响到用户体验,会导致订单系统、库存系统、供应链等等,产生的问题是一系列的连锁反应,所以电商都不希望超卖发生,但是在大并发的场景最容易发生的就是超卖,不同线程读取到的当前库存数据可能下个毫秒就被其他线程修改了,如果没有一定的锁库存机制那么库存数据必然出错,都不用上万并发,几十并发就可以导致商品超卖;

3)性能:当遇到大并发和超卖问题后,必然会引出另一个问题,那就是性能问题,如何保证在大并发请求下,系统能够有好的性能,让用户能够有更好的体验,不然每个用户都等几十秒才能知道结果,那体验必然是很糟糕的;

秒杀系统方案

对于上面提到的秒杀的问题,我们当时的架构是下面这样的:

从整个秒杀系统的架构其实和一般的互联网系统架构本身没有太多的不同,核心理念还是通过缓存、异步、限流来保证系统的高并发和高可用。下面从一笔秒杀交易的流程来描述下秒杀系统架构设计的要点:

1)对于大促时候的秒杀活动,一般运营会配置静态的活动页面,配置静态活动页面主要有两个目的一方面是为了便于在各种社交媒体分发,另一方面是因为秒杀活动页的流量是大促期间最大的,通过配置成静态页面可以将页面发布在公有云上动态的横向扩展;

2)将秒杀活动的静态页面提前刷新到CDN节点,通过CDN节点的页面缓存来缓解访问压力和公司网络带宽,CDN上缓存js、css和图片;

3)将活动H5页面部署在公有云的web server上,使用公有云最大的好处就是能够根据活动的火爆程度动态扩容而且成本较低,同时将访问压力隔离在公司系统外部;

4)在提供真正商品秒杀业务功能的app server上,需要进行交易限流、熔断控制,防止因为秒杀交易影响到其他正常服务的提供,我们在限流和熔断方面使用了hystrix,在核心交易的controller层通过hystrix进行交易并发限流控制,当交易流量超出我们设定的限流最大值时,会对新交易进行熔断处理固定返回静态失败报文。

5)服务降级处理,除了上面讲到的限流和熔断控制,我们还设定了降级开关,对于首页、购物车、订单查询、大数据等功能都会进行一定程度的服务降级,例如我们会对首页原先动态生成的大数据页面布局降级为所有人看到的是一样的页面、购物车也会降级为不在一级页面的tabbar上的购物车图标上显示商品数量、历史订单的查询也会提供时间周期较短的查询、大数据商品推荐也会提供一样的商品推荐,通过这样的降级处理能够很好的保证各个系统在大促期间能够正常的提供最基本的服务,保证用户能够正常下单完成付款。

6)上面介绍的都是如何保证能扛住高并发,下面介绍下整个方案中如何防止超卖现象的发生,我们日常的下单过程中防止超卖一般是通过在数据库上实施乐观锁来完成,使用乐观锁虽然比for update这种悲观锁方式性能要好很多,但是还是无法满足秒杀的上万并发需求,我们的方案其实也很简单实时库存的扣减在缓存中进行,异步扣减数据库中的库存,保证缓存中和数据库中库存的最终一致性。

在这个方案中我们使用的分布式缓存是redis,使用了codis集群方案稳定性和高可用方面还是比较有保证的,因为redis是单线程写,所以也不用担心线程安全的问题,redis自身就能够保证数据的强一致性,在下单的事务中包含了实时扣减缓存中的库存和异步发送队列,由队列处理器再异步从队列中取出订单根据订单信息扣减库存系统数据库中的商品数量。

总结

整个秒杀方案就介绍这么多,其实整个的思路还是比较简单的,也没有特别复杂的地方,对于大部分公司的高并发场景还是适用的,并且比较容易实施上线,该方案对于我们当时每秒几万的并发场景是能够扛住的,但是对于像小米、12306这些在高峰时动不动几十万用户并发的场景,使用这样的方案可能用户体验方面和系统服务方面就会存在一些问题了,对于每秒几十万并发的场景我们一般除了会在技术层面进行优化,更多的会通过其他一些业务手段来进行交易分流来分散整体的高并发访问,秒杀方案就介绍到这里,具体的实现这里就不写了,我相信跟着上面介绍的思路很容易就能将代码写出来。

其他不错文章推荐:https://blog.csdn.net/qq_28666081/article/details/83043215

                                https://blog.csdn.net/bigtree_3721/article/details/72760538

                                https://blog.csdn.net/a724888/article/details/81038138

猜你喜欢

转载自blog.csdn.net/wade3015/article/details/88413584