记录些Spring+题集(5)

系统稳定性建设

1. 背景

军演压测,故障演练,系统的梳理以检测系统的稳定性以应对高可用,高性能,高并发。我们知道系统的稳定性建设是贯穿整个研发流程:需求阶段,研发阶段,测试阶段,上线阶段,运维阶段;整个流程中的所有参与人员:产品,研发,测试,运维人员都应关注系统的稳定性。业务的发展及系统建设过程中,稳定性就是那个1,其他的是1后面的0,没有稳定性,就好比将万丈高楼建于土沙之上。

2. 研发阶段

研发阶段主要参与人员是研发,主要产出物是技术方案设计文档和代码,一个是研发阶段的开始,一个是研发阶段的结束,我们要把控好技术文档和代码质量,从而减少线下bug率及线上的故障;

2.1 技术方案

2.1.1 技术方案评审

技术文档的评审需要有本团队的架构师和相关研发,测试,产品,上下游系统的研发同学参与,这样能够最大限度的保证技术方案的实现和产品需求对齐,上下游系统同学也知道我们的实现,采取更加合理的交互方式,测试同学也可以从测试视角给出一些风险点建议,架构师可以确保我们的实现和业界最佳实践的差异,确保合理性,避免过度设计;我们所要做的是开放心态采取大家的意见,严控技术文档的质量;

技术文档的评审可以采用提问的方式,会议开始前可以将技术文档分享给大家,让大家先阅读10分钟,所有同学开始提问,技术文档设计人其实不用读自己的技术文档给大家介绍,只要将大家的问题回答完,并能够思考下大家的建议,合理的采纳后,其实技术文档的质量就有了很大的保证,有的同学在技术文档评审时,比较反感大家的提问,总感觉在挑战自己,有些问题回答不上来,其实可以换种思路:有些问题回答不上来是正常的,可以先将大家的建议采纳了,会后再思考下合理性;大家对自己技术方案是建言献策,是保证自己技术方案的质量,避免在技术方案阶段就存在重大的线上隐患。

2.1.2 技术方案关注点

当我们遇到一个问题的时候,首先要思考的这是一个新问题还是老问题,99.99%遇到的都是老问题,因为我们所从事的是工程技术,不是科学探索;我们所要做的就是看下国内外同行针对这个问题的解法,learn from best practices;所以技术方案的第一步是对标,学习最佳实践,这样能让我们避免走弯路;

同时根据奥卡姆剃刀原理,我们力求技术方案简单,避免过度设计,针对一个复杂的问题,我们的技术方案相对复杂些,简单的问题技术方案相对简单些,我们所要追求的是复杂的问题通过拆解划分,用一个个简单的技术方案解决掉。同时技术文档不仅关注功能的实现,更重要的是关注架构,性能,质量,安全;即如何打造一个高可用系统。打造一个高可用的系统是进行系统稳定性建设的前提,如果我们的系统都不能保证高可用,又谈何系统稳定系建设那,下面介绍下进行系统稳定性建设我们在技术方案中常用的方法及关注点。

2.1.2.1 限流

限流一般是从服务提供者provider的视角提供的针对自我保护的能力,对于流量负载超过我们系统的处理能力,限流策略可以防止我们的系统被激增的流量打垮。京东内部无论是同步交互的JSF, 还是异步交互的JMQ都提供了限流的能力,大家可以根据自己系统的情况进行设置;我们知道常见的限流算法包括:计数器算法,滑动时间窗口算法,漏斗算法,令牌桶算法,具体算法可以网上google下,下面是这些算法的优缺点对比。

图片

2.1.2.2 熔断降级

熔断和降级是两件事情,但是他们一般是结合在一起使用的。熔断是防止我们的系统被下游系统拖垮,比如下游系统接口性能严重变差,甚至下游系统挂了;这个时候会导致大量的线程堆积,不能释放占用的CPU,内存等资源,这种情况下不仅影响该接口的性能,还会影响其他接口的性能,严重的情况会将我们的系统拖垮,造成雪崩效应,通过打开熔断器,流量不再请求到有问题的系统,可以保护我们的系统不被拖垮。降级是一种有损操作,我们作为服务提供者,需要将这种损失尽可能降到最低,无论是返回友好的提示,还是返回可接受的降级数据。降级细分的话又分为人工降级,自动降级。

人工降级:人工降级一般采用降级开关来控制,公司内部一般采用配置中心Ducc来做开关降级,开关的修改也是线上操作,这块也需要做好监控

自动降级:自动降级是采用自动化的中间件例如Hystrix,公司的小盾龙等;如果采用自动降级的话;我们必须要对降级的条件非常的明确,比如失败的调用次数等;

2.1.2.3 超时

分布式系统中的难点之一:不可靠的网络,京东物流现有的微服务架构下,服务之间都是通过JSF网络交互进行同步通信,我们探测下游依赖服务是否可用的最快捷的方式是设置超时时间。超时的设置可以让系统快速失败,进行自我保护,避免无限等待下游依赖系统,将系统的线程耗尽,系统拖垮。

超时时间如何设置也是一门学问,如何设置一个合理的超时时间也是一个逐步迭代的过程,比如下游新开发的接口,一般会基于压测提供一个TP99的耗时,我们会基于此配置超时时间;老接口的话,会基于线上的TP99耗时来配置超时时间。

超时时间在设置的时候需要遵循漏斗原则,从上游系统到下游系统设置的超时时间要逐渐减少,如下图所示。为什么要满足漏斗原则,假设不满足漏斗原则,比如服务A调取服务B的超时时间设置成500ms,而服务B调取服务C的超时时间设置成800ms,这个时候回导致服务A调取服务B大量的超时从而导致可用率降低,而此时服务B从自身角度看是可用的;

2.1.2.4 重试

分布式系统中性能的影响主要是通信,无论是在分布式系统中还是垮团队沟通,communication是最昂贵的;比如我们研发都知道需求的交付有一半以上甚至更多的时间花在跨团队的沟通上,真正写代码的时间是很少的;分布式系统中我们查看调用链路,其实我们系统本身计算的耗时是很少的,主要来自于外部系统的网络交互,无论是下游的业务系统,还是中间件:Mysql, redis, es等等;

所以在和外部系统的一次请求交互中,我们系统是希望尽最大努力得到想要的结果,但往往事与愿违,由于不可靠网络的原因,我们在和下游系统交互时,都会配置超时重试次数,希望在可接受的SLA范围内一次请求拿到结果,但重试不是无限的重试,我们一般都是配置重试次数的限制,偶尔抖动的重试可以提高我们系统的可用率,如果下游服务故障挂掉,重试反而会增加下游系统的负载,从而增加故障的严重程度。在一次请求调用中,我们要知道对外提供的API,后面是有多少个service在提供服务,如果调用链路比较长,服务之间rpc交互都设置了重试次数,这个时候我们需要警惕重试风暴。如下图service D 出现问题,重试风暴会加重service D的故障严重程度。对于API的重试,我们还要区分该接口是读接口还是写接口,如果是读接口重试一般没什么影响,写接口重试一定要做好接口的幂等性。

图片

2.1.2.5 兼容

我们在对老系统,老功能进行重构迭代的时候,一定要做好兼容,否则上线后会出现重大的线上问题,公司内外有大量因为没有做好兼容性,而导致资损的情况。兼容分为:向前兼容性和向后兼容性,需要好好的区分他们,如下是他们的定义:

向前兼容性:向前兼容性指的是旧版本的软件或硬件能够与将来推出的新版本兼容的特性,简而言之旧版本软件或系统兼容新的数据和流量。

向后兼容性:向后兼容性则是指新版本的软件或硬件能够与之前版本的系统或组件兼容的特性,简而言之新版本软件或系统兼容老的数据和流量。

根据新老系统和新老数据我们可以将系统划分为四个象限:第一象限:新系统和新数据是我们系统改造上线后的状态,第三象限:老系统和老数据是我们系统改造上线前的状态,第一象限和第三象限的问题我们在研发和测试阶段一般都能发现排除掉,线上故障的高发期往往出现在第二和第四象限,第二象限是因为没有做好向前兼容性,例如上线过程中,发现问题进行了代码回滚,但是在上线过程中产生了新数据,回滚后的老系统不能处理上线过程中新产生的数据,导致线上故障。第四象限是因为没有做好向后兼容性,上线后新系统影响了老流程。针对第二象限的问题,我们可以构造新的数据去验证老的系统,针对第四象限的问题,我们可以通过流量的录制回放解决,录制线上的老流量,对新功能进行验证。

图片

2.1.2.6 隔离

隔离是将故障爆炸半径最小化的有效手段,在技术方案设计中,我们通过不同层面的隔离来控制影响范围:

2.1.2.6.1 系统层面隔离

我们知道系统的分类可以分为:在线的系统,离线系统(批处理系统),近实时系统(流处理系统),如下是这些系统的定义:

在线系统:服务端等待请求的到达,接收到请求后,服务尽可能快的处理,然后返回给客户端一个响应,响应时间通常是在线服务性能的主要衡量指标。我们生活中在手机使用的APP大部分都是在线系统;

离线系统:或称批处理系统,接收大量的输入数据,运行一个作业来处理数据,并产出输出数据,作业往往需要定时,定期运行一段时间,比如从几分钟到几天,所以用户通常不会等待作业完成,吞吐量是离线系统的主要衡量指标。例如我们看到的报表数据:日订单量,月订单量,日活跃用户数,月活跃用户数都是批处理系统运算一段时间得到的;

近实时系统:或者称流处理系统,其介于在线系统和离线系统之间,流处理系统一般会有触发源:用户的行为操作,数据库的写操作,传感器等,触发源作为消息会通过消息代理中间件:JMQ, KAFKA等进行传递,消费者消费到消息后再做其他的操作,例如构建缓存,索引,通知用户等;

以上三种系统是需要进行隔离建设的,因为他们的衡量指标及对资源的使用情况完全不一样的,比如我们小组会将在线系统作为一个服务单独部署:jdl-uep-main, 离线系统和近实时系统作为一个服务单独部署:jdl-uep-worker;

2.1.2.6.2 环境的隔离

从研发到上线阶段我们会使用不同的环境,比如业界常见的环境分为:开发,测试,预发和线上环境;研发人员在开发环境进行开发和联调,测试人员在测试环境进行测试,运营和产品在预发环境进行UAT,最终交付的产品部署到线上环境提供给用户使用。在研发流程中,我们部署时要遵循从应用层到中间件层再到存储层,都要在一个环境,严禁垮环境的调用,比如测试环境调用线上,预发环境调用线上等。

图片

2.1.2.6.3 数据的隔离

随着业务的发展,我们对外提供的服务往往会支撑多业务,多租户,所以这个时候我们会按照业务进行数据隔离;比如我们组产生的物流订单数据业务方就包含京东零售,其他电商平台,ISV等,为了避免彼此的影响我们需要在存储层对数据进行隔离,数据的隔离可以按照不同粒度,第一种是通过租户id字段进行区分,所有的数据存储在一张表中,另外一个是库粒度的区分,不同的租户单独分配对应的数据库。

图片

数据的隔离除了按照业务进行隔离外,还有按照环境进行隔离的,比如我们的数据库分为测试库,预发库,线上库,全链路压测时,我们为了模拟线上的环境,同时避免污染线上的数据,往往会创建影子库,影子表等。根据数据的访问频次进行隔离,我们将经常访问的数据称为热数据,不经常访问的数据称为冷数据;将经常访问的数据缓存到缓存,提高系统的性能。不经常访问的数据持久化到数据库或者将不使用的数据结转归档到

2.1.2.6.4 核心,非核心隔离

我们知道应用是分级的,京东内部针对应用的重要程度会将应用分为0,1,2,3级应用。业务的流程也分为黄金流程和非黄金流程。在业务流程中,针对不同级别的应用交互,需要将核心和非核心的流程进行隔离。例如在交易业务过程中,会涉及到订单系统,支付系统,通知系统,那这个过程中核心系统是订单系统和支付系统,而通知相对来说重要性不是那么高,所以我们会投入更多的资源到订单系统和支付系统,优先保证这两个系统的稳定性,通知系统可以采用异步的方式与其他两个系统解耦隔离,避免对其他另外两个系统的影响。

图片

2.1.2.6.5 读写隔离

应用层面,领域驱动设计(DDD)中最著名的CQRS(Command Query Responsibility Segregation)将写服务和读服务进行隔离。写服务主要处理来自客户端的command写命令,而读服务处理来自客户端的query读请求,这样从应用层面进行读写隔离,不仅可以提高系统的可扩展性,同时也会提高系统的可维护性,应用层面我们都采用微服务架构,应用层都是无状态服务,可以扩容加机器随意扩展,存储层需要持久化,扩展就比较费劲。除了应用层面的CQRS,在存储层面,我们也会进行读写隔离,例如数据库都会采用一主多从的架构,读请求可以路由到从库从而分担主库的压力,提高系统的性能和吞吐量。所以应用层面通过读写隔离主要解决可扩展问题,存储层面主要解决性能和吞吐量的问题。

图片

2.1.2.6.6 线程池隔离

线程是昂贵的资源,为了提高线程的使用效率,避免创建和销毁的消耗,我们采用了池化技术,线程池来复用线程,但是在使用线程池的过程中,我们也做好线程池的隔离,避免多个API接口复用同一个线程。

图片

2.2 代码Review

codeReview是研发阶段的最后一个流程,对线下的bug率和线上质量及稳定性有着重要的作用,针对于代码如何review,谈一些自己的看法:

形成团队代码风格:首先一个团队的代码应该形成该团队的代码风格,这样能够提高codeReview的效率及协作的效率,作为新加入的成员,应该遵循团队的代码风格规范。

Review的关注点:代码review切记不要陷入细节,主要以review代码风格为主,如果一个团队形成统一的代码风格,我们通过review风格就能将大部分问题发现,在关注功能的同时,再关注下性能,安全。

结对编程:在代码编写过程中,我们要培养结对编程的习惯,这样针对某次需求,codeReview时,熟悉该模块的同事把控下细节,架构师把控风格。

控制每次review代码量:每次提交代码进行review时,不要一次性提交review大量的代码,要将review的内容细分,比如一个方法的实现,一个类等。

开放心态:review的过程其实是学习提升的过程,通过代码review,虚心接收别人的意见,学习优雅代码的编写方式,提高自己的代码水平。

3 上线阶段

我们可以看下公司的故障管理平台白虎所记录的故障:发生系统故障一般都是外部对系统做了改变,往往发生在上线阶段:代码的部署,数据库的更改,配置中心的变动等;上线阶段是故障的高发期;一个系统不可能不出线上问题,我们所要追求的是,降低线上的故障频率,缩短故障恢复时间。针对上线过程出现问题,我们知道业界有著名的上线过程三板斧:可监控,可灰度,可回滚。

3.1 上线三板斧

3.1.1 可监控

上线的过程中,我们的系统要做到可监控,如果没有监控,上线过程中我们对系统的状态是一无所知,是很可怕的。监控什么东西那,其实监控的就是指标。这就涉及到指标的定义,指标我们分为业务指标和技术指标,技术指标又分为软件和硬件。业务指标一般是我们定义的观测业务变化情况的度量,例如订单量,支付量等。技术层面的软件指标:可用率,TP99, 调用量,技术层面的硬件指标:cpu 内存 磁盘 网络IO。目前我们二级部门在做OpsReview,主要review的是可用率,TP99,调用量这几个指标,分别对应系统的可用性,性能,并发。

做好这些指标的监控后,我们接下来需要做的是针对这些指标做好告警,如果某个指标突破设定的阈值后,需要进行告警通知给我们,针对监控告警指标阈值的设置,建议先严后松,即系统建设初始阶段设置的严格些,避免遗漏告警,出现线上问题,后续随着系统建设的迭代需要设置更合理的告警阈值,避免告警泛滥,造成狼来了的效应。总之上线发布过程的一段时间是事故和问题发生的高峰,这块一定做好指标监控,日志监控,对报警要敏感。

图片

3.1.2 可灰度

上线过程中,我们要做到可灰度,通过灰度执行变更以限制爆炸半径,降低影响范围,同时灰度过程要做好兼容。灰度分为不同维度的灰度:机器维度,机房维度,地域维度,业务维度:用户,商家,仓,承运商等。

机器维度:我们用行云部署时,可以每个分组先部署一部分机器进行灰度,灰度一段时间比如:24小时没什么问题后,再部署剩余的机器。

机房维度:微服务架构下,我们的应用会部署在不同的机房中,可以按照机房维度灰度,比如先部署发布代码在某个机房分组下,观察一段时间再按照比例扩大灰度机房范围直至全量。例如先部署中云信的机房,灰度一段时间后,再逐步灰度有孚的机房。

地域维度:现在的部署架构都是多机房互为灾备,异地多活,单元化部署,例如业界美团的外卖业务非常适合做异地多活,单元化部署,因为外卖业务的商户,用户,骑手天然具有聚合性,北京的用户大概率不会在上海点外卖,这样根据业务的属性,在系统建设的时候,从应用层到中间件层,再到存储层可以单元化部署在上海地域的机房和北京地域的机房,功能发布的时候可以灰度某个地域,做到地域级别的容灾。

业务维度:在上线过程中,我们也可以根据业务属性进行灰度,例如上线了某个功能或者产品,根据用户维度灰度,某些用户或者某些商户才能使用该功能,产品。

3.1.3 可回滚

线上出现问题时,我们应该优先止损,其次才是分析根因。止损的最快方式就是回滚,回滚分为代码回滚和数据回滚,代码回滚即将我们代码恢复到原有的逻辑,代码回滚有两种方式:开关控制和部署回滚。最快捷的方式是开关控制,一键开关打开或者关闭就可以实现回滚到原有的逻辑,操作成本最低,止损最快速。第二种方式就是部署回滚,通过发布平台,例如行云将代码回滚到上个稳定运行的版本。有时候我们代码回滚完,如果没有做好向前兼容性,系统应用依然有问题,例如上线过程中产生了新数据,回滚完后,代码不能处理新的数据。所以这个时候又涉及到数据的回滚,数据的回滚涉及到修数:将产生的新数据无效掉,或者修改为正确的数据等,当数据量比较大时,数据的回滚一般耗时费力,所以建议做好向前兼容性,直接代码回滚。

3.2 线上问题应对

3.2.1 常见问题分类

针对线上的问题,我们第一步是识别出是什么问题,然后才能解决问题,针对线上各种各样的问题我们可以进行聚合,归并分类下,针对每种问题去参考业界的处理方法和团队的内的紧急预案,做到临阵不乱。

3.2.2 问题生命周期

当出现问题时,我们也需要清楚一个线上问题的生命周期:从问题发生,到我们发现问题,进而进行响应处理,观测问题是否修复,服务是否恢复正常,到最终针对该问题进行复盘,当发生系统发生问题时,我们越早发现问题,对业务的影响越小。

3.2.3 如何预防问题

就像人的身体生病一样,当问题发生已经晚了,我们要投入更多时间和精力到如何预防中,就像扁鹊的大哥一样治未病,防患于未然。根据破窗原理,一个问题出现了,如果放任不管,问题的严重性会越来越大,直到不可挽回。我们可以从研发的规范,研发的流程,变更流程这几个方面进行预防。

图片

3.2.4 如何发现问题

对于一个系统,如果外界不对其做功,根据熵增原理,其会越来越混乱,直到出现问题,外界对其做功,就涉及到改变,因为改变是人在操作,由于各种不可控的因素,也会导致各种线上问题,所以我们可以看到对于一个系统上线后不出现问题是不可能的,当出现问题时,我们第一步是如何快速的发现问题?对于问题发现的渠道,工作中接触到的有如下几种:自我意识,监控告警,业务反馈;

自我意识:我们C2部门每周有一个重要会议OpsReview,各个C3团队会对个团队的核心接口的不规律跳点,毛刺进行可用率,性能,调用量的review,以通过这种主动的,自我意识行为发现潜在的线上问题。同时我们组每天早会的重要一项:UMP监控全域看板的review,我们会对昨天核心接口的可用率,TP99,调用量,进行分析的,对于可用率降低,TP99有毛刺,不规范的流量调用会进行排查原因,尽早自我发现问题,同时也会对机器的CPU, 内存使用率,Mysql, redis , es各种存储进行review。

监控告警:这是我们发现问题最常用的渠道,通过主动的监控指标,被动的接收告警来发现问题,告警指标我们分为业务指标和技术指标,具体分类可详见3.1.1可监控部分

业务反馈:这种发现问题的方式是我们最不愿意看到的,如果等到业务反馈,说明线上问题已经影响到用户,我们常常因为监控告警的缺失,漏报而导致落后于业务发现问题,所以我们最希望每个人,团队都有这种自我意识,将线上问题提早发现,防患于未然。

3.2.5 如何响应问题

出现线上问题后,我们个人对问题的认知是非常有限的,并且这个时候人处于一种高度紧张的状态,所以这个时候一定要群里周知自己的leader,将情况如实表达,不要夸大和缩小问题的范围和影响,同时将问题进行通告。整个问题的响应过程包含以下几步:

1.保留现场:问题发生的现场是我们排查问题的依据,所以要将现场的日志,数据等信息保存好,比如内存dump, 线程dump,避免机器重启后这些信息的丢失。

2.提供信息:提供自己所知道的信息,协助排查,不要扩大和缩小问题

3.恢复服务:当出现线上问题是,我们追求的是以最快的速度恢复服务,快速止损,业界有快速止血,恢复服务的几板斧:回滚:服务回滚,数据回滚,重启,扩容,禁用节点,功能降级

4.双重确认:服务恢复后,我们需要确认是否恢复了,可以通过观察:业务指标是否正常,技术指标是否正常,数据是否正常,日志是否正常等来观测问题的恢复情况

5.故障通告:确认问题没有什么问题后,需要再应急群中周知大家:业务人员,产品经理,系统的上下游,测试人员,SRE等。并让产品和业务进行确认,然后周知用户。

3.2.6 如何定位问题

服务恢复后,我们可以回过头来细致的分析下到底是什么原因导致了线上的问题。定位问题也要讲究方法论,这就涉及到定位问题三要素:知识,工具,方法。

知识:相对其他行业,计算机行业应该是知识更新迭代最快的行业,所以我们需要不断的去学习,更新自己的知识库,不给自己设限。例如你想解决FullGC问题,你必须对JVM进行系统的学习,想解决慢sql,必须对Mysql进行系统的学习,现在AI大模型这么火,我们也需要对prompt engineering, RAG , Agent, 多模态等进行学习了解。有了知识我们才能遇到问题时,知道是什么,为什么?

工具:工欲善其事,必先利其器,工程师要善于借助公司工具来提高解决问题的效率,熟练使用公司各种中间件工具,公司已经有的中间件,优先使用公司的中间件,公司内一个中间件团队维护的中间件工具要优于业务研发小组内维护的中间件工具,不要小组内部,或者团队内部重复造轮子,并且小组内人员的流动变更,容易造成中间件没人维护。下图是公司常用的中间件工具:

图片

方法:解决问题我们要讲究方法,选择正确的方法可以事半功倍,提高我们定位问题及解决问题的效率,下面是我们研发人员常见的排查问题的方法

图片

3.2.7 如何修复问题

有了知识,工具和方法后,其实我们很快的就定位到问题了,定位到问题后,我们就要想办法如何去把问题修复了,以下是问题修复的流程:

图片

3.2.8 如何复盘问题

问题发生后,我们需要从此次问题中分析根因,并汲取教训和经验,避免犯同样的错误。这就涉及到问题的复盘,如何进行问题的复盘那,一般会经过如下几个步骤:回顾目标,评价结果,分析原因,总结经验。例如我们C2部门每周的opsReview会议上都会有线上问题的复盘:coe,如何进行coe复盘谈一些自己的思考。

•参考业界的5WHY分析法剖析问题的根因

•5WHY分析法:5代表的是问题的深度,而不是问题的数量

•基于问题的答案继续进行提问,5个问题是有关联的,层层递进的,找到问题的根因

图片

https://www.cnblogs.com/Jcloud/p/18279366

10W qps高并发秒杀

1 秒杀系统的业务架构

1.1 秒杀系统的业务特点

秒杀,通常指的是在电商平台上进行的限时抢购活动,通常以极低的价格限量出售某些商品,吸引大量用户在短时间内进行购买。这些活动不仅能够大幅提升平台的销量和流量,还能增强用户黏性和平台影响力。

秒杀活动具有以下几个特点:

  1. 时间紧迫

    秒杀活动通常设置在特定的时间段内进行,时间限制通常非常短,从几分钟到几小时不等。用户必须在规定时间内完成购买,增加了活动的紧迫感和参与热情。

  2. 数量有限

    秒杀商品的数量通常非常有限,远少于潜在购买者的数量。这种稀缺性促使用户迅速采取行动,生怕错过机会。

  3. 价格极低

    秒杀商品的价格通常比市场价低得多,甚至可能低于成本价。超低的价格吸引了大量用户参与,形成抢购热潮。

  4. 竞争激烈

    由于时间紧迫和数量有限,用户之间的竞争非常激烈。很多人会提前准备,甚至使用抢购软件,尽一切可能在最短时间内完成购买。

  5. 营销效果强

    秒杀活动具有很强的营销效果。超低价格和紧迫感吸引了大量用户访问和参与,提升了品牌知名度和用户活跃度。即使用户没有成功购买,也可能被吸引到平台上的其他商品和活动中。

  6. 社交传播

    秒杀活动容易引发用户之间的讨论和分享。用户可能会通过社交媒体分享秒杀信息,进一步扩大活动的影响力和参与度。

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个核心特点,导致了 秒杀系统的流量特点 :

  • 瞬时凸峰 (流量突刺)

  • 漏斗模型

秒杀系统的并发量存在瞬时凸峰的特点,也叫做流量突刺现象。 可以使用下图来表示:

图片

比如, 小米秒杀系统的流量峰值

  1. 秒杀前访问量平稳:在秒杀活动开始前,小米秒杀系统的访问量相对平稳,没有显著变化。

  2. 秒杀时刻的并发量激增:秒杀活动通常在上午10点开始,活动开始时,系统的并发访问量会瞬时激增,导致流量出现突刺现象。

秒杀的流量特点,与12306网站的春运访问量特点类似。

来看12306的特点:

  1. 平时访问量平缓:在平常日子里,12306网站的访问量相对稳定,没有显著的波动。

  2. 春运期间访问量激增:每年春运时节,12306网站的访问量会出现瞬时突增的现象。大量用户在短时间内涌入网站,导致访问量急剧上升。

2.2 高并发 吞吐量 规划和评估

吞吐量评估

吞吐量评估是指我们需要评估好吞吐量,

我们这个系统,是为了应对一个什么体量的业务,这个业务请求量的平均值、高峰的峰值大概都在一个什么级别。

如果是新系统,那么就需要根据产品和运营同学对业务有一个大体的预估,然后开发同学根据产品给的数据再进行详细的评估。如果是老系统,那么就可以根据历史数据来评估。

评估的时候,要从一个整体角度来看全局的量级,然后再细化到每个子业务模块要承载的量级。

吞吐量规划

是指我们系统在设计的时候,就要能够初步规划好我们的系统大致能够抗多少的量级,比如是十万还是百万级别的请求量,或者更多。

不同的量级对应的系统架构的设计会完全不一样,尤其到了千万、亿级别的量级的时候,架构的设计会有很多的考量。

同时,吞吐量规划还涉及到,我们系统上下游的各个模块、依赖的存储、依赖的三方服务,分别需要多少资源,需要有一个相对可以量化的数据出来。

容量规划阶段,更多是要依靠自身和团队的经验,比如要了解我们的 log 的性能、redis 的性能、rpc 接口的性能、服务化框架的性能等等,然后根据各种组件的性能来综合评估自己设计的系统的整体性能情况。

2.3 QPS 预估(漏斗型)

QPS 预估(漏斗型),指的是一个真实的请求过来后,从接入层开始,分别经过了我们整个系统的哪些层级、哪些模块,然后每一个层级的 QPS 的量级分别有多少,从请求链路上来看,层级越往下,那么下游层级的量级应该会逐步减少的,因为每经过一个层级,都有可能会被各种条件过滤掉的一部分请求。

比如说进入商品详情 这个例子。

图片

QPS 预估(漏斗型)就是需要我们预估好每一个层级的量级,包括但不限于从服务、接口、分布式缓存等各个层面来预估,最后构成我们完整的 QPS 漏斗模型。

2.4 通过二八定律进行流量预估

图片

2.5 性能压测评估

容量评估和容量规划之后,我们还需要做一件事情,就是性能压测评估,最好是能够做到全链路压测。

性能压测的目的是为了确保你的容量规划是准确的,比如我设计的这个系统,我规划的是能够抗千万级别的请求,那么实际上,真的能够抗住吗 ?

这个在上线之前,首先要根据经验来判断,然后是一定要经过性能压测得出准确结论的。

性能压测要关注的指标很多,但是重点要关注是两个指标,一个是 QPS、一个是响应耗时,要确保压测的结果符合预期。

压测的步骤可以先分模块单独压测,最后如果情况允许,那么最好执行全链路压测。

3 秒杀的异步架构

图片

10Wqps或者100Wqps场景,采用的同步处理请求的方案,是万万不能的,一旦并发量真的上来了,他们所谓的秒杀系统的性能会急剧下降。

一定要采用异步架构

3.1 同步模式的下单流程

我们先来看一下秒杀系统在同步下单时的时序图:

图片

在同步下单流程中,用户发起秒杀请求后,商城服务需要依次执行以下步骤来处理秒杀请求的业务:

  1. 识别验证码是否正确

    商城服务需“火眼金睛”,判断用户提交的验证码是否正确,确保只有“名副其实”的用户才能继续操作。

  2. 判断活动是否已经结束

    验证当前秒杀活动是否已经“曲终人散”,防止用户在活动结束后依然发起请求。

  3. 验证访问请求是否处于黑名单

    在电商领域中,恶意竞争犹如“鬼蜮伎俩”。其他商家可能会通过不正当手段占用系统资源。

    此时,商城服务需要使用风控系统等实现黑名单机制,如“庖丁解牛”般识别并拦截恶意请求。为了简单,也可以使用拦截器统计访问频次,形成“黑名单”。

  4. 验证真实库存是否足够

    系统需“运筹帷幄”,验证商品的真实库存是否充足,确保能够支持本次秒杀活动的需求。

  5. 扣减缓存中的库存

    在秒杀业务中,商品库存等信息通常存放在缓存中。此时,需要“未雨绸缪”,验证并扣减秒杀活动商品的库存,确保“货真价实”。

  6. 计算秒杀的价格

    由于秒杀活动中的商品价格与真实价格存在差异,需要“精打细算”,计算商品的秒杀价格,确保用户享受到“物超所值”的优惠。

  7. 下订单

    将用户提交的订单信息“名正言顺”地保存到数据库中,确保每一笔交易都有据可查,如同“案牍劳形”般细致入微。

  8. 扣减真实库存

订单入库后,需要在商品的真实库存中扣除本次成功下单的商品数量,确保库存数据“货真价实”,不出现“巧妇难为无米之炊”的尴尬局面。

注意:在实际的秒杀场景中,如果系统涉及的业务更加复杂,还会涉及更多的业务操作。这里只是“管中窥豹”,列举了一些常见的业务操作。

同步模式,当用户发起秒杀请求时,由于系统每个业务流程都是串行执行的,整体上系统的性能不会太高,当并发量太高时,我们会为用户弹出下面的排队页面,来提示用户进行等待。

在秒杀系统中,这种同步处理下单的业务流程的方案是不可取的。

3.2 两阶段异步下单流程

既然同步下单流程的秒杀系统称不上真正的秒杀系统,那我们就需要采用异步的下单流程了。

异步的下单流程不会限制系统的高并发流量。

图片

在同步下单流程中,用户发起秒杀请求后,商城服务分为两个阶段处理请求的业务。

第一个阶段 预下单阶段:

  1. 识别验证码是否正确

    商城服务需“火眼金睛”,判断用户提交的验证码是否正确,确保只有“名副其实”的用户才能继续操作。

  2. 判断活动是否已经结束

    验证当前秒杀活动是否已经“曲终人散”,防止用户在活动结束后依然发起请求。

  3. 验证访问请求是否处于黑名单

    在电商领域中,恶意竞争犹如“鬼蜮伎俩”。其他商家可能会通过不正当手段占用系统资源。

    此时,商城服务需要使用风控系统等实现黑名单机制,如“庖丁解牛”般识别并拦截恶意请求。为了简单,也可以使用拦截器统计访问频次,形成“黑名单”。

  4. 验证真实库存是否足够

    系统需“运筹帷幄”,验证商品的真实库存是否充足,确保能够支持本次秒杀活动的需求。

  5. 扣减缓存中的库存

    在秒杀业务中,商品库存等信息通常存放在缓存中。此时,需要“未雨绸缪”,验证并扣减秒杀活动商品的库存,确保“货真价实”。

第二个阶段,正式下单阶段:

  1. 计算秒杀的价格

    由于秒杀活动中的商品价格与真实价格存在差异,需要“精打细算”,计算商品的秒杀价格,确保用户享受到“物超所值”的优惠。

  2. 下订单

    将用户提交的订单信息“名正言顺”地保存到数据库中,确保每一笔交易都有据可查,如同“案牍劳形”般细致入微。

  3. 扣减真实库存

第一个阶段 和 第二个阶段进行解耦, 是异步。

如果第一个阶段失败, 就没有必要进入第二阶段了。

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点)前几分钟,用户并发量才真正突增,达到秒杀时间点时,并发量会达到顶峰。

但由于这类活动是大量用户抢少量商品的场景,必定会出现狼多肉少的情况,所以其实绝大部分用户秒杀会失败,只有极少部分用户能够成功。

正常情况下,大部分用户会收到商品已经抢完的提醒,收到该提醒后,他们大概率不会在那个活动页面停留了,如此一来,用户并发量又会急剧下降。所以这个峰值持续的时间其实是非常短的,这样就会出现瞬时高并发的情况,下面用一张图直观的感受一下流量的变化:

图片

像这种瞬时高并发的场景,传统的系统很难应对,我们需要设计一套全新的系统。可以从以下几个方面入手:

  1. 动静分离架构

  2. CDN加速

  3. 缓存

  4. mq异步处理

  5. 限流

  6. 分布式锁

  7. 系统扩容:系统扩容包括垂直扩容和水平扩容,增加设备和机器配置,绝大多数的场景有效。

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 过期,如果这个时刻缓存同时失效,而有大量请求进来了,因未缓存数据,所以都去查询数据库了,数据库压力增大,最终就会导致雪崩。

图片

缓存雪崩 解决方案

缓存雪崩是三大缓存问题里最严重的一种,我们来看看怎么预防和处理。

  • 提高缓存可用性

  1. 集群部署:通过集群来提升缓存的可用性,可以利用Redis本身的Redis Cluster或者第三方集群方案如Codis等。

  2. 多级缓存:设置多级缓存,设置一级缓存本地 guava 缓存,第一级缓存失效的基础上再访问二级缓存 redis,每一级缓存的失效时间都不同。

  • 过期时间

  1. 均匀过期:为了避免大量的缓存在同一时间过期,可以把不同的 key 过期时间随机生成,避免过期时间太过集中。在原有的实效时间基础上增加一个碎挤汁,比如 1-5 分钟随机,降低缓存的过期时间的重复率,避免发生缓存集体实效。

  2. 热点数据永不过期。

  • 熔断降级

  1. 服务熔断:当缓存服务器宕机或超时响应时,为了防止整个系统出现雪崩,可以使用hystrix 类似的熔断,暂时停止业务服务访问db, 或者其他被依赖的服务,避免 MySQL 被打死。

  2. 服务降级:当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。

7 库存扣减场景下的数据一致性架构

8 秒杀的mq异步架构

9 秒杀高可用架构

10 秒杀的限流和熔断架构

11 秒杀的降级架构

12 存储层面的高可用架构

13 运维部署层面的架构

开发阶段-灰度发布、接口测试设计

14 秒杀的多IDC机房双活高可用架构

15 秒杀的应急预案架构

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值