千万级流量架构优化策略

一、导读

问题:

朋友自己做的项目稳步发展,但是受市场行情影响,访问量突然增多且不断地在进行增加,在这个情况下来咨询我应该怎么做。以下是我的一个简单思路,大家不妨一块来看一下。

做法:

1、了解现状

      架构、业务特点;

2、给出一个最简单有效的应急方案

     目标:改造时间短、可以短期内支撑住访问压力;

现状:

架构、业务都比较简单,只有一个热点业务。

技术架构:nginx+web集群(webserver)+主从数据库(MySQL)+Redis

业务:主要是一个热点业务。

方案:

1、CDN

      减少静态资源的访问压力;(自动选择离用户最近的cdn故此能够减轻服务压力并提高用户体验度)

2、业务拆分

      隔离热点,独立扩容;(将并发较高的业务独立拆分,使之即便挂掉了也不影响其他服务)

3、读写分离

      分散数据库压力。(提高读写效率,对于读数据库进行负载均衡)

以上只是简单的处理,后期想要长期发展自然还需要进一步的做系统改造,咱们第一步先解决现有问题。

峰值QPS=(日总PV数*80%)/(日总秒数*20%)

这个公式的概念是:一般情况下每天80%的并发量集中在20%的时间内。

(PV*0.8)/(3600*24*0.2)

PV*0.8/17280

PV=1000,0000

1000,0000*0.8/17280=463

峰值为463

假设每台服务器能够承受的并发量是100,那么我们所需的服务器是

463/100=5

所以千万级服务器我们所需的服务器大概是5台服务器就足够了,当然这只是一个大概的值。

并且峰值QPS为100还有很大的优化空间,所以千万级的流量并不像大家所想的那么可怕!

二、策略

而我们所应考虑的是什么呢?

数据访问达到千万级后,访问量的快速增加所带来的架构变化。所以我们要在战略上藐视敌人,战术上重视敌人。

架构的设计以及演进是非常系统的,我们今天就以下几个点来重点聊一聊。

核心策略:拆分、队列、缓存、降级、限流等几个核心策略。

拆分:网站架构的演进过程,怎么从单机结构演进到异地多集群的分布式架构。方便大家更好的了解拆分的思想。

队列:咱们主要看一下,如何使用队列解决分布式事务的问题。

缓存:在更新数据时,你是应该先更新数据库呢还是先更新缓存呢?

降级:降级的场景以及如何做好降级。

限流:限流算法。

2.1、拆分策略

架构演进历程:混沌状态--->各自独立--->集群化--->分布式--->多集群部署--->异地部署

混沌状态:所有东西都放在一起,目标是快速起步。

各自独立:各司其职,资源独占。

集群化:增加人手,和工作分派角色。

分布式改造:业务分布式拆分,服务组件采用分布式体系。

集群和分布式的概念:

集群:多个人干活,每个人干的活都一样。每个人都负责全流程。

分布式:任务拆分来做,一个人负责一项,流程化协同。

多集群部署:使用硬件负载均衡器做集群间的请求分派。

异地部署:在多个地区部署,使用DNS根据用户地理位置就近分派。

拆分的维度 :系统维度、功能维度、读写维度等。

系统维度:例如,做电商项目中,就拆分了商品系统、购物车系统、订单系统、日志系统等。

功能维度:例如,电商平台中的优惠券系统,还可以拆分为建券系统、领券系统、用券系统。

读写维度:例如,商品系统,读的量非常大,比写多得多,那么可以拆分为商品读服务、商品写服务。

2.2、队列策略

队列的核心:异步、平缓的处理。

案例:流量削峰

什么是流量削峰:瞬间流量巨大,比如秒杀场景下,几万人购买几百件商品。

削峰的目的:让服务器请求更加平缓,保护服务器资源。

直接调用模式:多个调用者一起请求,流量大的时刻产生调用高峰,被调用者压力大,有崩溃风险。

消息队列模式:所有调用请求都去排队,即使高峰期也不会对被调用者直接产生影响。(将同步请求转化为异步请求)相当于一个水库。

案例:分布式事务解决方案

案例场景:用户成功下单后,为用户增加相应的积分。

直接调用的问题:如果积分系统故障,订单系统不知道,已经生成的订单不好处理。(积分未正常累计或订单撤销后积分也未进行扣除)

分布式事务:需要保证订单系统、积分系统的执行结果一致,都成功,或者都失败。

改进:使用消息队列,从同步调用改为异步调用,可以不关心积分系统的状态,保证最终一致性。

问题:订单系统应该先写数据库,还是先发消息?

改进:把消息写入本地数据库的消息表,与订单操作放在一个事务里面。

问题:如何把消息发到消息队列?

改进:一个定时执行的后台程序,从消息表中获取发送状态未“未发送”的消息,发送给消息队列,消息的状态为“已发送”。

问题:消息重复发送怎么办?

改进:积分系统负责保证幂等性(多次同样的调用结果一致)。

分布式事务总结:

1、上游系统要保证消息不丢,是通过本地消息表和后台定时程序来实现的;

2、下游系统要保证消息不重复处理,也就是保证幂等性,可以通过判重表来实现。

适用场景:分布式事务的提交或回滚只取决于事务发起方,也就是无需回滚。

消息队列中还有很多模式来处理问题,之后我们再慢慢了解哦!

2.3、缓存策略

缓存类型:

客户端(浏览器、APP客户端):适用于对实时性不敏感的数据,商品详情、评价、广告等。

网络(CDN):网站静态资源的缓存,提高对用户的响应。(按流量收费,比较耗钱)。

接入层(nginx代理缓存)、应用层(Redis)。

Cache Aside与Read Write Through策略

Cache Aside策略:

先更新缓存还是数据库?

假设先更新数据库:

假设先更新缓存:

都会产生数据不一致。

Cache Aside策略:更新数据时不更新缓存,删除缓存中的数据。读取数据时,如果缓存中没有数据,从数据库中读取,更新到缓存中。

Cache Aside读写流程:

在上面的场景下是否可以先删除缓存?不可以!!!——读写不一致。

Read Write Through策略

核心:用户只与缓存打交道,由缓存和数据库通信,写入或读取数据。注意,这里有一个关键角色,就是“缓存组件,感觉有点像仓库管理员。

Read Write Through读写流程:

各自都有自己的弊端,所以我们要根据项目中的实际情况来进行。

缓存雪崩与缓存穿透解决方案

雪崩:大量数据同时失效,数据库压力过大。

解决方案:

为有效期增加随机值;

使用高可用分布式缓存;

热点数据永远不过期;

在缓存失效后,通过加锁或者队列来控制读数据库的线程数量。

缓存穿透:缓存中没有,需要访问数据库,这就是缓存穿透了。穿透后需要查询DB,量大的话就会压垮数据库。

解决方案:

缓存空值;

布隆过滤器。

缓存空值处理场景:正常请求不存在的数据,可能其他请求也会读取这个数据,为防止多次无效访问数据库,我们可以把这个空值也缓存will,用户下次再请求时,就直接返回空。

布隆过滤器处理场景:大量恶意请求不存在的数据,即使缓存空值也无效,大量请求读取数据库,需要使用布隆过滤器,帮助我们在不查数据库的情况下就知道此ID是否存在,把恶意请求直接过滤掉。

什么是布隆过滤器?

是由一个二进制数组和一个hash算法组成。建议大家具体了解一下,咱们这里就不赘述了。

如何使用布隆过滤器来解决缓存穿透的问题?

1、写入数据时,更新布隆过滤器;

2、查数据时,先查询布隆过滤器是否存在,如果不存在就直接返回空,如果存在,再去查询数据库。

布隆过滤器优点:除了能够解决以上问题外,还比较节省空间。

布隆过滤器缺点:

1、由于hash冲突,存在误判。例如hash(张三)=4,hash(李四)=4。那么可以使用多个hash函数一起计算提高精度。

2、不能删除。可以使用int数组计算。

2.4降级策略

降级的应用场景

功能降级

电商平台很普遍的推荐功能,可以提升销量,但不是购物的核心流程。

在系统压力大的时候,可以降级改为默认内容。

写服务降级

在较大数据量时,不更新数据库只更新缓存,把要写的数据放到消息队列,流量高峰过了以后,把消息队列里的数据回放到数据库。

降级的定义:整体负载、流量>阈值,为了保证重要的服务能正常运行:

1、拒绝部分请求;

2、延迟或暂停一些不重要的服务、任务。

降级的作用:

降级是系统保护的重要手段,保证系统的高可用。

简单理解,降级就是丢车保帅,保证系统核心功能的正常。

降级的实现方式

手动降级:使用开关配置,对系统中可降级的服务都设置好开关项。

就像一个总闸,可以预先把非核心的功能关闭,也可以在监控系统发现问题时人工介入。

配置文件实现开关配置:适用于系统部署结构简单的场景。系统自动监控配置文件的变化,配置文件变化后自动重新载入。

配置中心实现开关配置:适用于分布式系统,有统一配置中心,服务开关在配置中心定义。在配置中心界面修改相应参数,自动通知相应服务。实现技术有:zookeeper、Redis、consul、etcd等。

自动降级:超时降级(对于非核心服务,如果长时间响应慢,就可以自动降级)、限流降级(当触发了限流阈值时,可以使用暂时屏蔽的方式来进行降级)、统计失败次数降级(在一个时间段内,调用失败率超出阈值,自动降级)。当程序遇到这些情况的时候自动进行降级处理。

降级后的处理方案:使用默认值、兜底数据、缓存数据、排队页面、无货通知、错误页面等。

2.5限流策略

限流的应用场景

为什么要限流?

热门的旅游景点、地铁都会限制人流量,防止超出其接待能力。

对于系统,在应对高性能压力的场景时,限流已经成为了标配技术解决方案,保证了系统的平稳运行。

限流就是对请求进行限制,例如某一接口的请求限制为100个每秒,对超过限制的请求则不处理。

保护系统的手段:

缓存:提升系统的访问速度,增大系统处理能力;

降级:暂时屏蔽,过后打开;

限流:对稀缺资源限制请求量,限速、拒绝服务。

限流的常用算法

固定时间窗口算法:对每个时间窗口内的请求进行计数,如果计数器超过了限制数量,则本窗口内后续的请求都会被丢弃。当时间到达下一个窗口时,计数器重置。(但是无法应对两个窗口间的突发流量)

滑动时间窗口算法:记录在时间窗口内每个接口请求到达的时间点,判断当前时间窗口内的接口请求数是否小于限流值。

令牌桶算法:令牌以固定速率生成,如果令牌桶满了则多余的令牌会直接丢弃。当请求到达时,会尝试从令牌桶中提取令牌,取到令牌的请求可以执行。如果桶空了,那么尝试取令牌的请求会被直接丢弃。

漏桶算法:漏桶容量固定,按照固定的速率流出请求,可以任意速率流入请求。如果流入过快,会溢出,对其请求

限流形式

1、单机限流,每个应用实例自己本机实现限流;

2、分布式限流,一个应用集群统一做限流。

以上,大致是千万级流量的优化措施,当然方法还有很多,之后根据大家需要咱们慢慢来看。

发布了441 篇原创文章 · 获赞 1021 · 访问量 53万+

猜你喜欢

转载自blog.csdn.net/A_BlackMoon/article/details/105146640