FSAC未来超级架构师
架构师总动员
实现架构转型,再无中年危机
高并发下,如何设计秒杀系统?这是一个高频面试题。 在40岁老架构师 尼恩的读者交流群 (50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、shein 希音、shopee、百度、网易的面试资格,遇到很多很重要的面试题: 每秒上万次下单请求,秒杀如何处理? 高并发下,如何设计秒杀系统? 如何解决秒杀超卖? 前几天 小伙伴面试 shopee,这个问题 没有回答好,导致面试挂了。小伙伴面试完了之后,来求助尼恩:如何才能回答得很漂亮,才能 让面试官刮目相看、口水直流。 所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 "技术肌肉",让面试官爱到 "不能自已、口水直流" ,然后实现"offer直提"。 当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典》V145版本PDF集群,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。 最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,后台回复:领电子书 虽说秒杀只是一个促销活动,但对架构的要点非常多。下面给大家总结一下设计秒杀架构的16个架构要点, 组成一个《高并发秒杀圣经》。 10Wqps高并发秒杀架构,可以说16大架构杀招的大集结, 帮你秒变架构师 接下来,咱们一个一个的架构杀招 来展开和梳理
本文目录
- 1 秒杀系统的业务架构
-
1.1 秒杀系统的业务特点
-
1.2 秒杀暴露与秒杀按钮
-
1.3 秒杀的验证码设计
- 2 秒杀系统的流量架构
-
2.1 秒杀系统的流量特点
-
2.2 高并发 吞吐量 规划和评估
-
2.3 QPS 预估(漏斗型)
-
2.4 通过二八定律进行流量预估
-
2.5 性能压测评估
- 3 秒杀的异步架构
-
3.1 同步模式的下单流程
-
3.2 两阶段异步下单流程
-
3.3 三阶段异步下单流程
- 4 电商系统的分层架构
-
4.1 经典电商系统的分层架构
-
4.2 秒杀系统的分层架构
-
4.3 秒杀架构的一些特殊方案
- 5 秒杀的动静分离架构
- 6 秒杀的缓存架构
-
6.1 读多写少大杀器:缓存架构
-
6.2 缓存的三大经典问题
-
6.3 缓存击穿
-解决方案
-本地锁的问题
- 6.4 缓存穿透
-缓存穿透 带来的风险
-缓存穿透为什么会产生缓存穿透
-缓存穿透解决方案
-缓存穿透的一大利器:布隆过滤器
- 6.5 缓存雪崩
-缓存雪崩 带来的风险
-缓存雪崩 解决方案
- 7 库存扣减场景下的数据一致性架构
- 8 秒杀的mq异步架构
- 9 秒杀高可用架构
- 10 秒杀的熔断架构
- 11 秒杀的降级架构
- 12 存储层面的高可用架构
- 13 运维部署层面的架构
- 开发阶段-灰度发布、接口测试设计
- 14 秒杀的多IDC机房双活高可用架构
- 15 秒杀的应急预案架构
- 说在最后:有问题找老架构取经
1 秒杀系统的业务架构
1.1 秒杀系统的业务特点
秒杀,通常指的是在电商平台上进行的限时抢购活动,通常以极低的价格限量出售某些商品,吸引大量用户在短时间内进行购买。 天猫、京东、拼多多等,都会定期举行秒杀活动,特别是在双十一、618等大型购物节期间。 这些活动不仅能够大幅提升平台的销量和流量,还能增强用户黏性和平台影响力。 秒杀活动具有以下几个特点:
-
时间紧迫: 秒杀活动通常设置在特定的时间段内进行,时间限制通常非常短,从几分钟到几小时不等。用户必须在规定时间内完成购买,增加了活动的紧迫感和参与热情。
-
数量有限: 秒杀商品的数量通常非常有限,远少于潜在购买者的数量。这种稀缺性促使用户迅速采取行动,生怕错过机会。
-
价格极低: 秒杀商品的价格通常比市场价低得多,甚至可能低于成本价。超低的价格吸引了大量用户参与,形成抢购热潮。
-
竞争激烈: 由于时间紧迫和数量有限,用户之间的竞争非常激烈。很多人会提前准备,甚至使用抢购软件,尽一切可能在最短时间内完成购买。
-
营销效果强: 秒杀活动具有很强的营销效果。超低价格和紧迫感吸引了大量用户访问和参与,提升了品牌知名度和用户活跃度。即使用户没有成功购买,也可能被吸引到平台上的其他商品和活动中。
-
社交传播: 秒杀活动容易引发用户之间的讨论和分享。用户可能会通过社交媒体分享秒杀信息,进一步扩大活动的影响力和参与度。
1.2 秒杀暴露与秒杀按钮
将符合条件的秒杀暴露给用户,以便互联网用户能参与商品的秒杀。这个操作可以由商户手动完成,在生产场景下,更合理的方式是系统自动维护。 大部分用户怕错过 秒杀时间点 ,一般会提前进入活动页面。此时看到的 秒杀按钮 是置灰,不可点击的,秒杀还没有暴露。 但此时很多用户已经迫不及待了,通过不停刷新页面,争取在第一时间看到秒杀按钮的点亮。 从前面得知,该活动页面是静态的。那么我们在静态页面中如何控制秒杀按钮,只在秒杀时间点时才点亮呢? 只有到了秒杀时间点那一时刻,秒杀已经暴露,秒杀暴露主要是生成一个 具备实效性的 exposed-key,通过动态秒杀JS异步获取 exposed-key,秒杀按钮才会自动点亮,变成可点击的。 另外,在客户端这一层的用户交互上需要具备一定的控制用户行为和禁止重复秒杀的能力。 比如,当用户提交秒杀请求之后,可以将秒杀按钮置灰,禁止重复提交。 1、秒杀开始前,秒杀按钮灰掉为"未开始",不可点击。 2、URL在活动开始前不可露出或者生效,否则容易被使用工具绕过浏览器提前下单。 导致活动还未开始,已经开始下单这个大黑洞。最好做法是在活动开始前,通过JS文件露出下单的URL。 3、在秒杀进行中,秒杀按钮才可以点击下单。 4、秒杀产品的介绍,详情,参数等等,全部静态化,将秒杀商品页面缓存在CDN上(如果没有CDN,也可以放在Nginx中做动静分离) 5、用户点击"下单"后,按钮置灰,禁止用户重复提交请求,限制用户在60秒之内只能提交一次请求。(防止DDOS攻击,后面有讲) 6、然后就发送请求了,请求统一发送到Nginx中 此外,前端还可以加一个定时器,控制比如:60秒之内,只允许发起一次请求。 如果用户点击了一次秒杀按钮,则在60秒之内置灰,不允许再次点击,等到过了时间限制,又允许重新点击该按钮。 秒杀暴露的动作是定时完成的,只有到了秒杀时间点,才开始暴露。 并且,也只有到了秒杀时间点,用户主动点了秒杀按钮才允许访问服务端。
这样能过滤大部分无效请求。
1.3 秒杀的验证码设计
在秒杀活动中添加验证码是为了防止恶意刷单和机器人攻击,确保活动的公平性和安全性。 加验证码的方式 ,同样能限制用户的访问频次,同时和限流不同,加验证码不会存在误杀的情况。 通常情况下,用户在请求之前,需要先输入验证码。 用户发起请求之后,服务端会去校验该验证码是否正确。 只有正确才允许进行下一步操作,否则直接返回,并且提示验证码错误。 此外,验证码一般是一次性的,同一个验证码只允许使用一次,不允许重复使用。 普通验证码,由于生成的数字或者图案比较简单,可能会被破解。 普通验证码 优点是生成速度比较快,缺点是有安全隐患。 还有一个验证码叫做:移动滑块,它生成速度比较慢,但比较安全,是目前各大互联网公司的首选。
2 秒杀系统的流量架构
2.1 秒杀系统的流量特点
秒杀系统的3个核心特点: 秒杀的特点一:限时、限量、限价
-
限时:秒杀活动如同"昙花一现",在规定的时间内进行。举例来说,活动仅限于某天上午10点到10点半,过时不候。
-
限量:商品数量如"凤毛麟角",秒杀活动中商品的数量有限,譬如只有10万件,售完为止。
-
限价:价格低廉,犹如"白菜价"。商品价格远远低于原来的价格,例如1元购等业务场景,吸引众多用户争先恐后。
-
这些限制条件可以"单打独斗",也可以"联袂登场",相辅相成,增加活动的吸引力和紧迫感。
秒杀的特点二:活动预热
-
提前配置:活动需要未雨绸缪,提前配置好各项内容,做到"未雨绸缪"。
-
信息展示:活动尚未开始时,用户可以查看相关信息,做到"心中有数"。
-
大力宣传:在秒杀活动开始前,广而告之,宣传造势,做到"声势浩大"。
秒杀的特点三:持续时间短
-
大量用户:购买人数如"过江之鲫",商品会迅速售罄。
-
高并发访问:系统流量如"井喷"般激增,并发访问量极高。大多数秒杀场景下,商品在"转瞬之间"即被抢购一空,宛如"争分夺秒"的竞技场。
由于秒杀系统的3个核心特点,导致了 秒杀系统的流量特点 :
-
瞬时凸峰 (流量突刺)
-
漏斗模型
秒杀系统的并发量存在瞬时凸峰的特点,也叫做流量突刺现象。 可以使用下图来表示:
比如, 小米秒杀系统的流量峰值
-
秒杀前访问量平稳:在秒杀活动开始前,小米秒杀系统的访问量相对平稳,没有显著变化。
-
秒杀时刻的并发量激增:秒杀活动通常在上午10点开始,活动开始时,系统的并发访问量会瞬时激增,导致流量出现突刺现象。
秒杀的流量特点,与12306网站的春运访问量特点类似。 来看12306的特点:
-
平时访问量平缓:在平常日子里,12306网站的访问量相对稳定,没有显著的波动。
-
春运期间访问量激增:每年春运时节,12306网站的访问量会出现瞬时突增的现象。大量用户在短时间内涌入网站,导致访问量急剧上升。
2.2 高并发 吞吐量 规划和评估
吞吐量评估 吞吐量评估是指我们需要评估好吞吐量, 我们这个系统,是为了应对一个什么体量的业务,这个业务请求量的平均值、高峰的峰值大概都在一个什么级别。 如果是新系统,那么就需要根据产品和运营同学对业务有一个大体的预估,然后开发同学根据产品给的数据再进行详细的评估。如果是老系统,那么就可以根据历史数据来评估。 评估的时候,要从一个整体角度来看全局的量级,然后再细化到每个子业务模块要承载的量级。 吞吐量规划 是指我们系统在设计的时候,就要能够初步规划好我们的系统大致能够抗多少的量级,比如是十万还是百万级别的请求量,或者更多。 不同的量级对应的系统架构的设计会完全不一样,尤其到了千万、亿级别的量级的时候,架构的设计会有很多的考量。 同时,吞吐量规划还涉及到,我们系统上下游的各个模块、依赖的存储、依赖的三方服务,分别需要多少资源,需要有一个相对可以量化的数据出来。 容量规划阶段,更多是要依靠自身和团队的经验,比如要了解我们的 log 的性能、redis 的性能、rpc 接口的性能、服务化框架的性能等等,然后根据各种组件的性能来综合评估自己设计的系统的整体性能情况。
2.3 QPS 预估(漏斗型)
QPS 预估(漏斗型) ,指的是一个真实的请求过来后,从接入层开始,分别经过了我们整个系统的哪些层级、哪些模块,然后每一个层级的 QPS 的量级分别有多少,从请求链路上来看,层级越往下,那么下游层级的量级应该会逐步减少的,因为每经过一个层级,都有可能会被各种条件过滤掉的一部分请求。 比如说进入商品详情 这个例子。 QPS 预估(漏斗型)就是需要我们预估好每一个层级的量级,包括但不限于从服务、接口、分布式缓存等各个层面来预估,最后构成我们完整的 QPS 漏斗模型。
2.4 通过二八定律进行流量预估
具体请参见 尼恩Java 高并发核心编程 卷3 :
2.5 性能压测评估
容量评估和容量规划之后,我们还需要做一件事情,就是性能压测评估,最好是能够做到全链路压测。 性能压测的目的是为了确保你的容量规划是准确的,比如我设计的这个系统,我规划的是能够抗千万级别的请求,那么实际上,真的能够抗住吗 ? 这个在上线之前,首先要根据经验来判断,然后是一定要经过性能压测得出准确结论的。 性能压测要关注的指标很多,但是重点要关注是两个指标,一个是 QPS、一个是响应耗时,要确保压测的结果符合预期。 压测的步骤可以先分模块单独压测,最后如果情况允许,那么最好执行全链路压测。
3 秒杀的异步架构
10Wqps或者100Wqps场景,采用的同步处理请求的方案,是万万不能的,一旦并发量真的上来了,他们所谓的秒杀系统的性能会急剧下降。 一定要采用异步架构
3.1 同步模式的下单流程
我们先来看一下秒杀系统在同步下单时的时序图: 在同步下单流程中,用户发起秒杀请求后,商城服务需要依次执行以下步骤来处理秒杀请求的业务:
-
识别验证码是否正确 商城服务需"火眼金睛",判断用户提交的验证码是否正确,确保只有"名副其实"的用户才能继续操作。
-
判断活动是否已经结束 验证当前秒杀活动是否已经"曲终人散",防止用户在活动结束后依然发起请求。
-
验证访问请求是否处于黑名单 在电商领域中,恶意竞争犹如"鬼蜮伎俩"。其他商家可能会通过不正当手段占用系统资源。 此时,商城服务需要使用风控系统等实现黑名单机制,如"庖丁解牛"般识别并拦截恶意请求。为了简单,也可以使用拦截器统计访问频次,形成"黑名单"。
-
验证真实库存是否足够 系统需"运筹帷幄",验证商品的真实库存是否充足,确保能够支持本次秒杀活动的需求。
-
扣减缓存中的库存 在秒杀业务中,商品库存等信息通常存放在缓存中。此时,需要"未雨绸缪",验证并扣减秒杀活动商品的库存,确保"货真价实"。
-
计算秒杀的价格 由于秒杀活动中的商品价格与真实价格存在差异,需要"精打细算",计算商品的秒杀价格,确保用户享受到"物超所值"的优惠。
-
下订单 将用户提交的订单信息"名正言顺"地保存到数据库中,确保每一笔交易都有据可查,如同"案牍劳形"般细致入微。
-
扣减真实库存
订单入库后,需要在商品的真实库存中扣除本次成功下单的商品数量,确保库存数据"货真价实",不出现"巧妇难为无米之炊"的尴尬局面。 注意:在实际的秒杀场景中,如果系统涉及的业务更加复杂,还会涉及更多的业务操作。这里只是"管中窥豹",列举了一些常见的业务操作。 同步模式,当用户发起秒杀请求时,由于系统每个业务流程都是串行执行的,整体上系统的性能不会太高,当并发量太高时,我们会为用户弹出下面的排队页面,来提示用户进行等待。 如果12306、淘宝、天猫、京东、小米等大型商城的秒杀系统是这么玩的话,那么,他们的系统迟早会被玩死,他们的系统工程师不被开除才怪! 所以,在秒杀系统中,这种同步处理下单的业务流程的方案是不可取的。
3.2 两阶段异步下单流程
既然同步下单流程的秒杀系统称不上真正的秒杀系统,那我们就需要采用异步的下单流程了。 异步的下单流程不会限制系统的高并发流量。 在同步下单流程中,用户发起秒杀请求后,商城服务分为两个阶段处理请求的业务。 第一个阶段 预下单阶段:
-
识别验证码是否正确 商城服务需"火眼金睛",判断用户提交的验证码是否正确,确保只有"名副其实"的用户才能继续操作。
-
判断活动是否已经结束 验证当前秒杀活动是否已经"曲终人散",防止用户在活动结束后依然发起请求。
-
验证访问请求是否处于黑名单 在电商领域中,恶意竞争犹如"鬼蜮伎俩"。其他商家可能会通过不正当手段占用系统资源。 此时,商城服务需要使用风控系统等实现黑名单机制,如"庖丁解牛"般识别并拦截恶意请求。为了简单,也可以使用拦截器统计访问频次,形成"黑名单"。
-
验证真实库存是否足够 系统需"运筹帷幄",验证商品的真实库存是否充足,确保能够支持本次秒杀活动的需求。
-
扣减缓存中的库存 在秒杀业务中,商品库存等信息通常存放在缓存中。此时,需要"未雨绸缪",验证并扣减秒杀活动商品的库存,确保"货真价实"。
第二个阶段,正式下单阶段:
-
计算秒杀的价格 由于秒杀活动中的商品价格与真实价格存在差异,需要"精打细算",计算商品的秒杀价格,确保用户享受到"物超所值"的优惠。
-
下订单 将用户提交的订单信息"名正言顺"地保存到数据库中,确保每一笔交易都有据可查,如同"案牍劳形"般细致入微。
-
扣减真实库存
第一个阶段 和 第二个阶段进行解耦, 是异步。 如果第一个阶段失败, 就没有必要进入第二阶段了。
3.3 三阶段异步下单流程
由于下单阶段很耗时, 在服务端 还可以解耦: 引入了MQ异步处理机制,同时可以返回 response 给前端, 可以让io线程解除阻塞, 去处理下一个请求。 前端可以短轮询查询秒杀结果。 短轮询查询秒杀结果 采用短轮询查询秒杀结果时,在页面上我们同样可以提示用户排队处理中,但是此时客户端会每隔几秒轮询服务器查询秒杀结果,相比于同步下单流程来说,无需长时间占用请求连接。
4 电商系统的分层架构
4.1 经典电商系统的分层架构
在电商领域,存在着典型的秒杀业务场景,那何谓秒杀场景呢。 简单的来说就是一件商品的购买人数远远大于这件商品的库存,而且这件商品在很短的时间内就会被抢购一空。 比如每年的618、双11大促,小米新品促销等业务场景,就是典型的秒杀业务场景。 我们可以将电商系统的架构简化成下图所示:
由图所示,我们可以简单的将电商系统的核心层分为:接入层、服务层和持久层。 接下来,我们就预估下每一层的并发量。
-
假如流量接入层使用的是高性能的Nginx,则我们可以预估Nginx最大的并发度为:10W+,这里是以万为单位。
-
假设服务层我们使用的是Tomcat,而Tomcat的最大并发度可以预估为800左右,这里是以百为单位。
-
假设持久层的缓存使用的是Redis,数据库使用的是MySQL,MySQL的最大并发度可以预估为1000左右,以千为单位。Redis的最大并发度可以预估为5W左右,以万为单位。
4.2 秒杀系统的分层架构
从分层的角度来说,秒杀系统架构可以分成3层,大致如下: 1)客户端:负责内容提速和交互控制。 2)接入层:负责认证、负载均衡、限流。 3)业务层:负责保障秒杀的数据一致性。 1. 客户端负责内容提速和交互控制 客户端需要完成秒杀商品的静态化展示。无论是在桌面浏览器还是移动端APP上展示秒杀商品,秒杀商品的图片和文字元素都需要尽可能静态化,尽量减少动态元素,这样就可以通过CDN来提速和抗峰值。 另外,在客户端这一层的用户交互上需要具备一定的控制用户行为和禁止重复秒杀的能力。比如,当用户提交秒杀请求之后,可以将秒杀按钮置灰,禁止重复提交。 2. 接入层负责认证、负载均衡、限流 秒杀系统的特点是并发量极大,但实际的优惠商品有限,秒杀成功的请求数量很少,所以如果不在接入层进行拦截,则大量请求会造成数据库连接耗尽、服务端线程耗尽,导致整体雪崩。因此,必须在接入层进行用户认证、负载均衡、接口限流。 对于总流量较小的系统,可以在内部网关(如Zuul)完成认证、负载均衡、接口限流的功能,具体的分层架构如图11-2所示。
图11-2 内部网关(如Zuul)完成认证、负载均衡、接口限流 对于总流量较大的系统,会有一层甚至多层外部网关,因此,限流的职责会从内部网关剥离到外部网关,内部网关(如Zuul)仍然具备权限认证、负载均衡的能力,具体的分层架构如图11-3所示。
图11-3 外部网关与内部网关相结合完成认证、负载均衡、接口限流 3. 业务层负责保障数据一致性 秒杀的业务逻辑主要是下订单和减库存,都是数据库操作。大家都知道,数据库层只能承担"能力范围内"的访问请求,是最脆弱的一层,也是需要进行事务保护的一层。在业务层,还需要防止超出库存的秒杀(超卖和少买),为了安全起见,可以使用分布式锁对秒杀的数据库操作进行保护。
4.3 秒杀架构的一些特殊方案
一般在 秒杀时间点(比如:12点)前几分钟,用户并发量才真正突增,达到秒杀时间点时,并发量会达到顶峰。 但由于这类活动是大量用户抢少量商品的场景,必定会出现狼多肉少的情况,所以其实绝大部分用户秒杀会失败,只有极少部分用户能够成功。 正常情况下,大部分用户会收到商品已经抢完的提醒,收到该提醒后,他们大概率不会在那个活动页面停留了,如此一来,用户并发量又会急剧下降。所以这个峰值持续的时间其实是非常短的,这样就会出现瞬时高并发的情况,下面用一张图直观的感受一下流量的变化: 像这种瞬时高并发的场景,传统的系统很难应对,我们需要设计一套全新的系统。可以从以下几个方面入手:
-
动静分离架构
-
CDN加速
-
缓存
-
mq异步处理
-
限流
-
分布式锁
-
系统扩容:系统扩容包括垂直扩容和水平扩容,增加设备和机器配置,绝大多数的场景有效。
5 秒杀的动静分离架构
活动页面是用户流量的第一入口,所以是并发量最大的地方。 如果这些流量都能直接访问服务端,恐怕服务端会因为承受不住这么大的压力,而直接挂掉。 活动页面绝大多数内容是固定的,比如:商品名称、商品描述、图片等。 为了减少不必要的服务端请求,通常情况下,会对活动页面做静态化处理。 用户浏览商品等常规操作,并不会请求到服务端。 为了性能考虑 动静分离,分离出下面的两大部分:
-
静态 秒杀页面资源:一个html文件,包括 css、js和图片等,内容包括秒杀产品的介绍,详情,参数等等。静态秒杀静态资源文件提前缓存到CDN上,让用户能够就近访问秒杀页面。
-
动态秒杀的exposed-key,通过JS异步获取。秒杀暴露就是将符合条件的秒杀 暴露给用户,以便互联网用户能参与商品的秒杀。这个操作可以是商户手动完成,生产场景下的更合理的方式是系统定时任务去完成。秒杀暴露主要是生成一个 具备实效性的 exposed-key。
但只做页面静态化还不够,因为用户分布在全国各地,有些人在北京,有些人在成都,有些人在深圳,地域相差很远,网速各不相同。 如何才能让用户最快访问到活动页面呢? 这就需要使用CDN,它的全称是Content Delivery Network,即内容分发网络。 使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。 CDN服务器就是内容分发网络,把资源内容放在了全国各地的各服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,一般都是到阿里云买CDN服务器。 尼恩提示:如果没有CDN,也可以放在Nginx中做动静分离。
6 秒杀的缓存架构
秒杀的开始前,系统一般会访问秒杀详情,这个是高并发读的场景。 在下单的过程中,系统一般会先查一下库存是否足够,如果足够才允许下单,写数据库。如果不够,则直接返回该商品已经抢完。 在下单的过程中, 大量用户 抢 少量商品,只有极少部分用户能够抢成功。 所以绝大部分用户在秒杀时,库存其实是不足的,系统会直接返回该商品已经抢完。 这是非常典型的:读多写少 的场景。
6.1 读多写少大杀器:缓存架构
如果有数十万的请求过来,同时通过数据库查缓存是否足够,此时数据库可能会挂掉。 因为数据库的连接资源非常有限,比如:mysql,无法同时支持这么多的连接。 而应该改用缓存,比如:redis。 即便用了redis,也需要部署多个节点。
6.2 缓存的三大经典问题
通常情况下,我们需要在redis中保存商品信息,里面包含:商品id、商品名称、规格属性、库存等信息,同时数据库中也要有相关信息,毕竟缓存并不完全可靠。 用户在点击秒杀按钮,请求秒杀接口的过程中,需要传入的商品id参数,然后服务端需要校验该商品是否合法。 大致流程如下图所示: 根据商品id,先从缓存中查询商品,如果商品存在,则参与秒杀。 如果不存在,则需要从数据库中查询商品,如果存在,则将商品信息放入缓存,然后参与秒杀。 如果商品不存在,则直接提示失败。 这个过程表面上看起来是OK的,但是如果深入分析一下会发现一些问题。
6.3 缓存击穿
某个 key 设置了过期时间,但在正好失效的时候,有大量请求进来了,导致请求都到数据库查询了。
解决方案
大量并发时,只让一个请求可以获取到查询数据库的锁,其他请求需要等待,查到以后释放锁,其他请求获取到锁后,先查缓存,缓存中有数据,就不用查数据库。 比如商品A第一次秒杀时,缓存中是没有数据的,但数据库中有。 虽说上面有如果从数据库中查到数据,则放入缓存的逻辑。 然而,在高并发下,同一时刻会有大量的请求,都在秒杀同一件商品,这些请求同时去查缓存中没有数据,然后又同时访问数据库。 结果悲剧了,数据库可能扛不住压力,直接挂掉。 如何解决这个问题呢?这就需要加锁。
本地锁的问题
本地锁只能锁定当前服务的线程,如下图所示,部署了多个题目微服务,每个微服务用本地锁进行加锁。 本地锁在一般情况下没什么问题,但是当用来锁库存就有问题了:
-
1.当前总库存为 100,被缓存在 Redis 中。
-
2.库存微服务 A 用本地锁扣减库存 1 之后,总库存为 99。
-
3.库存微服务 B 用本地锁扣减库存 1 之后,总库存为 99。
-
4.那库存扣减了 2 次后,还是 99,就超卖了 1 个。
那如何解决本地加锁的问题呢? 使用分布式锁。
当然,针对这种情况,最好在项目启动之前,先把缓存进行预热。 即事先把所有的商品,同步到缓存中,这样商品基本都能直接从缓存中获取到,就不会出现缓存击穿的问题了。 是不是上面加锁这一步可以不需要了? 表面上看起来,确实可以不需要。但如果缓存中设置的过期时间不对,缓存提前过期了,或者缓存被不小心删除了,如果不加速同样可能出现缓存击穿。 其实这里加锁,相当于买了一份保险。
6.4 缓存穿透
如果有大量的请求传入的商品id,在缓存中和数据库中都不存在,这些请求不就每次都会穿透过缓存,而直接访问数据库了。 缓存穿透指一个一定不存在的数据,由于缓存未命中这条数据,就会去查询数据库,数据库也没有这条数据,所以返回结果是 null。 如果每次查询都走数据库,则缓存就失去了意义,就像穿透了缓存一样。
缓存穿透 带来的风险
利用不存在的数据进行攻击,数据库压力增大,最终导致系统崩溃。
缓存穿透为什么会产生缓存穿透
-
业务层误操作:缓存中的数据和数据库中的数据被误删除了,所以缓存和数据库中都没有数据;
-
恶意攻击:专门访问数据库中没有的数据。
缓存穿透解决方案
-
对结果 null 进行缓存,并加入短暂的过期时间。
-
使用布隆过滤器快速判断数据是否存在,避免从数据库中查询数据是否存在,减轻数据库压力。
-
前端进行请求检测。把恶意的请求(例如请求参数不合理、请求参数是非法值、请求字段不存在)直接过滤掉,不让它们访问后端缓存和数据库。
缓存穿透的一大利器:布隆过滤器
这时可以想到 布隆过滤器 。 系统根据商品id,先从布隆过滤器中查询该id是否存在,如果存在则允许从缓存中查询数据,如果不存在,则直接返回失败。 虽说该方案可以解决缓存穿透问题,但是又会引出另外一个问题:布隆过滤器中的数据如何更缓存中的数据保持一致? 这就要求,如果缓存中数据有更新,则要及时同步到布隆过滤器中。如果数据同步失败了,还需要增加重试机制,而且跨数据源,能保证数据的实时一致性吗?显然是不行的。所以布隆过滤器绝大部分使用在缓存数据更新很少的场景中。
6.5 缓存雪崩
某⼀时刻发⽣⼤规模的缓存失效的情况,例如缓存服务宕机、大量key在同一时间过期,这样的后果就是⼤量的请求进来直接打到DB上,db无响应,最后可能导致整个系统的崩溃,称为雪崩。 对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求, 但是缓存机器意外发生了:
-
缓存全盘宕机,缓存挂了,
-
大量key在同一时间过期
此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后db无响应,最后导致整个系统的崩溃。 此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。
缓存雪崩 带来的风险
尝试找到大量 key 同时过期的时间,在某时刻进行大量攻击,数据库压力增大,最终导致系统崩溃。 缓存雪崩是指我们缓存多条数据时,采用了相同的过期时间,比如 00:00:00 过期,如果这个时刻缓存同时失效,而有大量请求进来了,因未缓存数据,所以都去查询数据库了,数据库压力增大,最终就会导致雪崩。
缓存雪崩 解决方案
缓存雪崩是三大缓存问题里最严重的一种,我们来看看怎么预防和处理。
- 提高缓存可用性
-
集群部署:通过集群来提升缓存的可用性,可以利用Redis本身的Redis Cluster或者第三方集群方案如Codis等。
-
多级缓存:设置多级缓存,设置一级缓存本地 guava 缓存,第一级缓存失效的基础上再访问二级缓存 redis,每一级缓存的失效时间都不同。
- 过期时间
-
均匀过期:为了避免大量的缓存在同一时间过期,可以把不同的 key 过期时间随机生成,避免过期时间太过集中。在原有的实效时间基础上增加一个碎挤汁,比如 1-5 分钟随机,降低缓存的过期时间的重复率,避免发生缓存集体实效。
-
热点数据永不过期。
- 熔断降级
-
服务熔断:当缓存服务器宕机或超时响应时,为了防止整个系统出现雪崩,可以使用hystrix 类似的熔断,暂时停止业务服务访问db, 或者其他被依赖的服务,避免 MySQL 被打死。
-
服务降级:当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。
7 库存扣减场景下的数据一致性架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
8 秒杀的mq异步架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
9 秒杀高可用架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
10 秒杀的熔断架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
11 秒杀的降级架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
12 存储层面的高可用架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
13 运维部署层面的架构
开发阶段-灰度发布、接口测试设计
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
14 秒杀的多IDC机房双活高可用架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF
15 秒杀的应急预案架构
尼恩提示: 文章太长,这个大杀招,下一篇给大家发布,也可以找尼恩领取 《秒杀圣经》PDF