网易首页 > 网易号 > 正文 申请入驻

超详细的秒杀架构设计

0
分享至

  秒杀系统相信很多人见过,比如京东或者淘宝的秒杀,小米手机的秒杀,那么秒杀系统的后台是如何实现的呢?我们如何设计一个秒杀系统呢?对于秒杀系统应该考虑哪些问题?如何设计出健壮的秒杀系统?本文我们就来探讨一下这个问题。

  秒杀应该考虑哪些问题 超卖问题

  分析秒杀的业务场景,最重要的有一点就是超卖问题,假如备货只有100个,但是最终超卖了200,一般来讲秒杀系统的价格都比较低,如果超卖将严重影响公司的财产利益,因此首当其冲的就是解决商品的超卖问题。

  高并发

  秒杀具有时间短、并发量大的特点,秒杀持续时间只有几分钟,而一般公司都为了制造轰动效应,会以极低的价格来吸引用户,因此参与抢购的用户会非常的多。短时间内会有大量请求涌进来,后端如何防止并发过高造成缓存击穿或者失效,击垮数据库都是需要考虑的问题。

  接口防刷

  现在的秒杀大多都会出来针对秒杀对应的软件,这类软件会模拟不断向后台服务器发起请求,一秒几百次都是很常见的,如何防止这类软件的重复无效请求,防止不断发起的请求也是需要我们针对性考虑的。

  秒杀 URL

  对于普通用户来讲,看到的只是一个比较简单的秒杀页面,在未达到规定时间,秒杀按钮是灰色的,一旦到达规定时间,灰色按钮变成可点击状态。这部分是针对小白用户的,如果是稍微有点电脑功底的用户,会通过F12看浏览器的network看到秒杀的url,通过特定软件去请求也可以实现秒杀。或者提前知道秒杀url的人,一请求就直接实现秒杀了。这个问题我们需要考虑解决。

  数据库设计

  秒杀有把我们服务器击垮的风险,如果让它与我们的其他业务使用在同一个数据库中,耦合在一起,就很有可能牵连和影响其他的业务。如何防止这类问题发生,就算秒杀发生了宕机、服务器卡死问题,也应该让它尽量不影响线上正常进行的业务。

  大量请求问题

  按照「高并发」的考虑,就算使用缓存还是不足以应对短时间的高并发的流量的冲击。如何承载这样巨大的访问量,同时提供稳定低时延的服务保证,是需要面对的一大挑战。我们来算一笔账,假如使用的是 Redis 缓存,单台 Redis 服务器可承受的 QPS 大概是 4W 左右,如果一个秒杀吸引的用户量足够多的话,单 QPS 可能达到几十万,单体 Redis 还是不足以支撑如此巨大的请求量。缓存会被击穿,直接渗透到 DB,从而击垮MySQL,后台会将会大量报错。

  秒杀系统的设计和技术方案 秒杀系统数据库设计

  针对「数据库设计」提出的秒杀数据库的问题,因此应该单独设计一个秒杀数据库,防止因为秒杀活动的高并发访问拖垮整个网站。这里只需要两张表,一张是秒杀订单表,一张是秒杀货品表。

  其实应该还有几张表,商品表:可以关联goods_id查到具体的商品信息,商品图像、名称、平时价格、秒杀价格等,还有用户表:根据用户user_id可以查询到用户昵称、用户手机号,收货地址等其他额外信息,这个具体就不给出示例了。

  秒杀 URL 的设计

  为了避免有程序访问经验的人通过下单页面url直接访问后台接口来秒杀货品,我们需要将秒杀的 URL 实现动态化,即使是开发整个系统的人都无法在秒杀开始前知道秒杀的URL 。具体的做法就是通过 md5 加密一串随机字符作为秒杀的 URL,然后前端访问后台获取具体的 URL,后台校验通过之后才可以继续秒杀。

  秒杀页面静态化

  将商品的描述、参数、成交记录、图像、评价等全部写入到一个静态页面,用户请求不需要通过访问后端服务器,不需要经过数据库,直接在前台客户端生成,这样可以最大可能的减少服务器的压力。具体的方法可以使用freemarker模板技术,建立网页模板,填充数据,然后渲染网页。

  单体 Redis 升级为集群 Redis

  秒杀是一个读多写少的场景,使用 Redis 做缓存再合适不过。不过考虑到缓存击穿问题,我们应该构建 Redis 集群,采用哨兵模式,可以提升Redis的性能和可用性。

  使用 Nginx

  Nginx 是一个高性能 Web 服务器,它的并发能力可以达到几万,而 Tomcat 只有几百。通过 Nginx 映射客户端请求,再分发到后台 Tomcat 服务器集群中可以大大提升并发能力。

  精简 SQL

  典型的一个场景是在进行扣减库存的时候,传统的做法是先查询库存,再去update。这样的话需要两个SQL,而实际上一个SQL我们就可以完成的。可以用这样的做法:update miaosha_goods set stock =stock-1 where goos_id ={#goods_id} and version = #{version} and sock>0;这样的话,就可以保证库存不会超卖并且一次更新库存,还有注意一点这里使用了版本号的乐观锁,相比较悲观锁,它的性能较好。

  Redis 预减库存

  很多请求进来,都需要后台查询库存,这是一个频繁读的场景。可以使用Redis来预减库存,在秒杀开始前可以在 Redis 设值,比如 redis.set(goodsId,100),这里预放的库存为100可以设值为常量,每次下单成功之后,Integer stock = (Integer)redis.get(goosId); 然后判断 sock 的值,如果小于常量值就减去1;不过注意当取消的时候,需要增加库存,增加库存的时候也得注意不能大于之间设定的总库存数(查询库存和扣减库存需要原子操作,此时可以借助 lua 脚本)下次下单再获取库存的时候,直接从Redis里面查就可以了。

  接口限流

  秒杀最终的本质是数据库的更新,但是有很多大量无效的请求,我们最终要做的就是如何把这些无效的请求过滤掉,防止渗透到数据库。限流的话,需要入手的方面很多:

  前端限流

  首先第一步就是通过前端限流,用户在秒杀按钮点击以后发起请求,那么在接下来的5秒是无法点击(通过设置按钮为disable)。这一小举措开发起来成本很小,但是很有效。

  同一个用户xx秒内重复请求直接拒绝

  具体多少秒需要根据实际业务和秒杀的人数而定,一般限定为10秒。具体的做法就是通过Redis的键过期策略,首先对每个请求都从 String value = redis.get(userId);如果获取到这个 value 为空或者为 null,表示它是有效的请求,然后放行这个请求。如果不为空表示它是重复性请求,直接丢掉这个请求。如果有效,采用redis.setexpire(userId,value,10).value 可以是任意值,一般放业务属性比较好,这个是设置以 userId 为 key,10秒的过期时间(10秒后,key对应的值自动为null)。

  令牌桶算法限流

  接口限流的策略有很多,我们这里采用令牌桶算法。令牌桶算法的基本思路是每个请求尝试获取一个令牌,后端只处理持有令牌的请求,生产令牌的速度和效率我们都可以自己限定,Guava 提供了 RateLimter 的 API 供我们使用。以下做一个简单的例子,注意需要引入Guava:

  public class TestRateLimiter {
public static void main(String[] args) {//1秒产生1个令牌final RateLimiter rateLimiter = RateLimiter.create(1);for (int i = 0; i < 10; i++) {//该方法会阻塞线程,直到令牌桶中能取到令牌为止才继续向下执行。double waitTime= rateLimiter.acquire();System.out.println("任务执行" + i + "等待时间" + waitTime);}System.out.println("执行结束");}}

  上面代码的思路就是通过RateLimiter来限定我们的令牌桶每秒产生1个令牌(生产的效率比较低),循环10次去执行任务。acquire会阻塞当前线程直到获取到令牌,也就是如果任务没有获取到令牌,会一直等待。那么请求就会卡在我们限定的时间内才可以继续往下走,这个方法返回的是线程具体等待的时间。执行如下:

  可以看到任务执行的过程中,第1个是无需等待的,因为已经在开始的第1秒生产出了令牌。接下来的任务请求就必须等到令牌桶产生了令牌才可以继续往下执行。如果没有获取到就会阻塞(有一个停顿的过程)。不过这个方式不太好,因为用户如果在客户端请求,如果较多的话,直接后台在生产token就会卡顿(用户体验较差),它是不会抛弃任务的,我们需要一个更优秀的策略:如果超过某个时间没有获取到,直接拒绝该任务。接下来再来个案例:

  public class TestRateLimiter2 {
public static void main(String[] args) {final RateLimiter rateLimiter = RateLimiter.create(1);
for (int i = 0; i < 10; i++) {long timeOut = (long) 0.5;boolean isValid = rateLimiter.tryAcquire(timeOut, TimeUnit.SECONDS);System.out.println("任务" + i + "执行是否有效:" + isValid);if (!isValid) {continue;}System.out.println("任务" + i + "在执行");}System.out.println("结束");}}

  其中用到了tryAcquire方法,这个方法的主要作用是设定一个超时的时间,如果在指定的时间内预估(注意是预估并不会真实的等待),如果能拿到令牌就返回true,如果拿不到就返回false。然后我们让无效的直接跳过,这里设定每秒生产1个令牌,让每个任务尝试在0.5秒获取令牌,如果获取不到,就直接跳过这个任务(放在秒杀环境里就是直接抛弃这个请求)。程序实际运行如下:

  只有第1个获取到了令牌,顺利执行了,下面的基本都直接抛弃了,因为0.5秒内,令牌桶(1秒1个)来不及生产就肯定获取不到返回false了。

  这个限流策略的效率有多高呢?假如我们的并发请求是400万瞬间的请求,将令牌产生的效率设为每秒20个,每次尝试获取令牌的时间是0.05秒,那么最终测试下来的结果是,每次只会放行4个左右的请求,大量的请求会被拒绝,这就是令牌桶算法的优秀之处。

  异步下单

  为了提升下单的效率,并且防止下单服务的失败。需要将下单这一操作进行异步处理。最常采用的办法是使用队列,队列最显著的三个优点:异步、削峰、解耦。这里可以采用 RabbitMQ,在后台经过了限流、库存校验之后,流入到这一步骤的就是有效请求。然后发送到队列里,队列接受消息,异步下单。下完单,入库没有问题可以用短信通知用户秒杀成功。假如失败的话,可以采用补偿机制,重试。

  服务降级

  假如在秒杀过程中出现了某个服务器宕机,或者服务不可用,应该做好后备工作。之前的博客里有介绍通过Hystrix进行服务熔断和降级,可以开发一个备用服务,假如服务器真的宕机了,直接给用户一个友好的提示返回,而不是直接卡死,服务器错误等生硬的反馈。

  总结

  秒杀流程图:

  这就是我设计出来的秒杀流程图,当然不同的秒杀体量针对的技术选型都不一样,这个流程可以支撑起几十万的流量,如果是成千万破亿那就得重新设计了。比如数据库的分库分表、队列改成用Kafka、Redis增加集群数量等手段。通过本次设计主要是要表明的是我们如何应对高并发的处理,并开始尝试解决它,在工作中多思考、多动手能提升我们的能力水平,加油!如果本篇博客有任何错误,请麻烦指出来,不胜感激。

  来源: https://www.cnblogs.com/wyq178/p/11261711.html

  深入理解 RabbitMQ 的前世今生

  高可用Redis服务架构分析与搭建

  架构师所需要的硬实力和软技能

  好的IT系统架构师是怎样的?

  顶级业务架构设计的“道”与“术”

  如何画好架构图

  免责声明:

  本公众号部分分享的资料来自网络收集和整理,所有文字和图片版权归属于原作者所有,且仅代表作者个人观点,与本公众号无关,文章仅供读者学习交流使用,并请自行核实相关内容,如文章内容涉及侵权,请联系后台管理员删除。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
2024年:北京市解除劳动合同经济补偿基数上限封顶数额提高至47103元,565239元内的补偿免税。

2024年:北京市解除劳动合同经济补偿基数上限封顶数额提高至47103元,565239元内的补偿免税。

周晨律师
2024-06-20 00:03:40
民政部发布公告,浙江2个机场名称确定

民政部发布公告,浙江2个机场名称确定

浙江发布
2024-06-19 16:05:55
中国海警的立威之战,让菲律宾和美国不敢启动《共同防御条约》?

中国海警的立威之战,让菲律宾和美国不敢启动《共同防御条约》?

战域笔墨
2024-06-19 23:42:55
意外!申花刚在中超丢掉榜首位置,两大主力就主动撂挑子官宣离队

意外!申花刚在中超丢掉榜首位置,两大主力就主动撂挑子官宣离队

评球论事
2024-06-19 21:16:10
回顾广东初二男生遭五人暴殴,其父铁棍打施暴者,117秒棍砸45次

回顾广东初二男生遭五人暴殴,其父铁棍打施暴者,117秒棍砸45次

吾爱纪实
2024-06-19 09:49:28
向佐为2岁儿子庆生并道歉,自曝与儿子初次见面时,向笙已7个月大

向佐为2岁儿子庆生并道歉,自曝与儿子初次见面时,向笙已7个月大

娱絮
2024-06-18 21:07:07
销量降至0.21万辆!第一批小米车主,已经开始卖车了:“刚提车时恨不得每天住在车里,后来只能忍痛卖车”【附新能源汽车行业现状分析】

销量降至0.21万辆!第一批小米车主,已经开始卖车了:“刚提车时恨不得每天住在车里,后来只能忍痛卖车”【附新能源汽车行业现状分析】

前瞻网
2024-06-19 14:13:10
噩耗:他于6月19日去世。出身“中国最牛家族”,干出七个第一

噩耗:他于6月19日去世。出身“中国最牛家族”,干出七个第一

华人星光
2024-06-19 16:23:29
谭咏麟病愈后首次公开现身,瘦到青筋毕现感慨声线不好

谭咏麟病愈后首次公开现身,瘦到青筋毕现感慨声线不好

小萝卜天下事
2023-07-21 21:57:53
隔空回应比亚迪?长城李瑞峰:车市有人出老千我们就要掀桌子

隔空回应比亚迪?长城李瑞峰:车市有人出老千我们就要掀桌子

IT之家
2024-06-20 08:18:11
106国参加瑞士和会:中方拒绝参会,与世界文明为伍,勿忘雅尔塔

106国参加瑞士和会:中方拒绝参会,与世界文明为伍,勿忘雅尔塔

大风文字
2024-06-03 10:27:47
宋祖儿,被彻底封杀!

宋祖儿,被彻底封杀!

听风听你
2024-06-18 18:37:24
范加尔:我接受了25次化疗和激素注射后,足协打电话过来让我带队

范加尔:我接受了25次化疗和激素注射后,足协打电话过来让我带队

直播吧
2024-06-19 20:56:12
刚刚,姜萍被打假了!

刚刚,姜萍被打假了!

财经要参
2024-06-19 08:30:26
熟人杀熟能狠到什么地步?网友:丈夫含泪看妻子走进他人家门

熟人杀熟能狠到什么地步?网友:丈夫含泪看妻子走进他人家门

奇特短尾矮袋鼠
2024-06-19 19:15:32
叫嚣有“外交豁免权”的大妈,掀开了特权阶层的底裤

叫嚣有“外交豁免权”的大妈,掀开了特权阶层的底裤

观风者
2024-06-19 08:44:32
毁三观!82年大龄剩女相亲要18万8彩礼谈崩,网友:还敢要彩礼?

毁三观!82年大龄剩女相亲要18万8彩礼谈崩,网友:还敢要彩礼?

三月柳
2024-06-17 12:03:57
沙特想要的,中国给了!183万人朝觐麦加,中国“无人小巴”亮相

沙特想要的,中国给了!183万人朝觐麦加,中国“无人小巴”亮相

影孖看世界
2024-06-20 06:50:02
王思聪女儿疑似蝴蝶手,一岁多还不会说话,黄一鸣回应惹争议!

王思聪女儿疑似蝴蝶手,一岁多还不会说话,黄一鸣回应惹争议!

古希腊掌管月桂的神
2024-06-19 16:50:48
4大强敌进死亡之组!女排最差一组出炉,网友:中国队坐山观虎斗

4大强敌进死亡之组!女排最差一组出炉,网友:中国队坐山观虎斗

体坛知识分子
2024-06-20 06:05:02
2024-06-20 10:22:44
IT架构师联盟
IT架构师联盟
IT架构实战分享
689文章数 7654关注度
往期回顾 全部

科技要闻

美国AI圈震动! “OpenAI宫斗”核心人物苏茨克维官宣创业

头条要闻

美媒:真主党有十几万枚炮弹 或3天摧毁以基础设施

头条要闻

美媒:真主党有十几万枚炮弹 或3天摧毁以基础设施

体育要闻

绿军的真老大,开始备战下赛季了

娱乐要闻

黄一鸣“杀疯了” 直播间卖大葱养孩子

财经要闻

茅台大跌,谁的锅?

汽车要闻

双肾格栅变化大/内饰焕新 新一代宝马X3官图发布

态度原创

健康
艺术
手机
教育
家居

晚餐不吃or吃七分饱,哪种更减肥?

艺术要闻

穿越时空的艺术:《马可·波罗》AI沉浸影片探索人类文明

手机要闻

早报:苹果重要源代码被盗 谷歌调整手机快充标准

教育要闻

2025最佳留学城市排名出炉!伦敦连续霸榜6年,美国竟挤不进前十?

家居要闻

自然开放 实现灵动可变空间

无障碍浏览 进入关怀版