架构设计系列文章
高并发,往往意味着大的流量,而大流量必然会对系统带来冲击。面对这种大流量的冲击,有的系统抗住了,有的系统被冲垮了。而抗住了的系统,一定程度上就是我们所谓的高并发系统。
简单来说,我们 所谓的高并发系统,就是一个面对大流量的冲击还能平稳运行的系统。
那如何理解高并发呢?
从对高并发系统的定义中可以看出,所谓的高并发,就是如何让系统面对大流量的冲击还能平稳运行。
那么,高并发到底是什么呢?
高并发,通常是指通过设计保证系统能够同时并行处理很多请求。
我们常见的高并发场景有:淘宝的双11、春运时的抢票、微博大V的热点新闻等。除了这些典型事情,每秒几十万请求的秒杀系统、每天千万级的订单系统、每天亿级日活的信息流系统等,都可以归为高并发。
很显然,上面谈到的高并发场景,并发量各不相同,那么,到底多大并发才算高并发呢?
不能只看数字,要看具体的业务场景。
不能说10W QPS的秒杀是高并发,而1W QPS的信息流就不是高并发。信息流场景涉及复杂的推荐模型和各种人工策略,它的业务逻辑可能比秒杀场景复杂10倍不止。因此,不在同一个维度,没有任何比较意义。
业务都是从0到1做起来的,并发量和QPS只是参考指标
在业务量逐渐变成原来的10倍、100倍的过程中,你是否用到了高并发的处理方法去演进你的系统,从架构设计、编码实现、甚至产品方案等维度去预防和解决高并发引起的问题?而不是一味的升级硬件、加机器做水平扩展。
各个高并发场景的业务特点完全不同:有读多写少的信息流场景、有读多写多的交易场景。因此,高并发本身是没有具体标准的,要结合具体的业务场景来看。
高并发绝不意味着只追求高性能。从宏观角度看,高并发系统的设计目标有三个:高性能、高可用,以及高扩展。就是所谓的“三高”,三高不是孤立的,而是相互支撑的。
高性能体现系统的并行处理能力。 在有限的资源投入下,提高性能意味着节省成本。同时,性能也反映了用户体验,响应时间100毫秒和1秒,给用户的感受是完全不同的。
通常,对一个系统进行性能优化,我们首先要对系统性能瓶颈进行分析,然后再遵循二八原则,抓主要矛盾进行优化。
高可用表示系统连续提供服务的能力。 一个全年不停机、无故障;另一个隔三差五出线上事故、宕机,用户肯定选择前者。另外,如果系统只能做到90%可用,也会大大拖累业务。
高可用的方案主要从冗余、取舍、系统运维3个方向考虑,同时需要有配套的值班机制和故障处理流程,当出现线上问题时,可及时跟进处理。
高扩展表示系统的扩展能力。 流量高峰时能否在短时间内完成扩容,更平稳地承接峰值流量,比如双11活动、明星离婚等热点事件。
这3个目标是需要通盘考虑的,因为它们互相关联、甚至也会相互影响。
比如:考虑系统的扩展能力,你会将服务设计成无状态的,这种集群设计保证了高扩展性,其实也间接提升了系统的性能和可用性。
再比如:为了保证可用性,通常会对服务接口进行超时设置,以防大量线程阻塞在慢请求上造成系统雪崩,那超时时间设置成多少合理呢?一般,我们会参考依赖服务的性能表现进行设置。
再从微观角度来看,高性能、高可用和高扩展又有哪些具体的指标来衡量?为什么会选择这些指标呢?
通过性能指标可以度量系统目前存在的性能问题,同时作为性能优化的评估依据。
常用的一些性能指标如下:
1. 平均响应时间(平均RT)
响应时间是指从请求发出到收到响应花费的时间。该指标和用户体验息息相关,监控系统一般都有三个RT,即平均、最大、最小。平均响应时间,是最常用的指标,但是缺陷很明显,对于慢请求不敏感。
比如,1万次请求,其中9900次是1ms,100次是100ms,则平均响应时间为1.99ms,虽然平均耗时仅增加了0.99ms,但是1%请求的响应时间已经增加了100倍。
2. 分位值(TP90/TP99)
分位值指将响应时间从小到大排序,取前 N% 位置的值,即为该分位的值,分位值越大,对慢请求越敏感。
比如,TP90 表示排在第 90 分位的响应时间。相比平均响应时间,TP90,TP99等指标更能体现系统的稳定性。
比如,收集100个数据,从小到大排列,TP95 分位值就是取出第95个请求的响应时间做统计。 TP50分位值就是第50个请求的响应时间。如果能够保证 TP95 分位值的稳定性,就能很好的体现系统的稳定性。
3. 每秒查询率(QPS)/ 每秒处理事务数(TPS)
QPS即每秒查询率,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。QPS = 请求数/秒,即每秒的响应请求数,也即是最大吞吐能力。
TPS即系统每秒处理的事务数量,一个事务是指一个客户端向服务器发送请求然后服务器做出反应的过程。
一般,评价系统性能均以每秒钟完成的交易数量来衡量。系统整体处理能力取决于处理能力最低模块的TPS值。
QPS基本类似于TPS,但不同的是,一个TPS可以包含多个QPS。对于一个页面的一次访问,形成一个TPS;但一次页面请求,可能产生多次对服务器的请求,服务器对这些请求,就可计入“QPS”之中。
比如,访问一个页面会请求服务器3次,此时,TPS=1 、QPS=3。
4. 并发数
并发数指系统同时能处理的请求数量,反映系统的负载能力。
计算公式:
比如,请求一个 index.html 页面,客户端发起了3个请求,此时,TPS=1 、QPS=3 、并发数=3。
注意,QPS不等于并发数,QPS是每秒HTTP请求数量,并发数是系统同时处理的请求数量。
5. 吞吐量(Throughput)
吞吐量指系统在单位时间内处理的请求数量,反映系统的承压能力,通常由QPS和并发数决定。
吞吐量和响应时间呈反比,比如响应时间是1ms,则吞吐量为每秒1000次。
一个系统的吞吐量与请求对CPU的消耗,外部系统接口,IO等等紧密关联。单个请求对CPU消耗越高,外部系统接口IO响应速度越慢,系统吞吐能力越低,反之越高。当某一项达到最高值,系统吞吐量就上不去了,假设压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换、内存等等其他消耗导致系统性能下降。
6. 综合浏览量(PV)
PV即页面浏览量或者点击量,一个访客在24小时内访问的页面数量。
7. 独立访客 (UV)
UV即一定时间范围内相同访客多次访问网站,只计算为一个独立的访客。
8. 最后,如何设定性能目标?
通常,对于高并发系统,设定性能目标时,必须同时考虑吞吐量、平均RT和TP分位值。
比如,每秒1万次请求,平均RT控制在50ms以下,TP99控制在100ms以下。
另外,从用户体验角度来看,200毫秒被认为是第一个分界点,用户感觉不到延迟,1秒是第二个分界点,用户能感受到延迟,但是可以接受。因此,对于一个健康的高并发系统,TP99应该控制在200毫秒以内,TP999或者TP9999应该控制在1秒以内。
可用性是一个可以量化的指标,行业内一般用 几个9 来表示系统的可用性指标。
计算公式:可用性 = 正常运行时间 / 系统总运行时间 × 100%。
下面来看看系统不同可用性等级的具体指标。
可用性等级 | 年故障时间 | 日故障时间 | 描述 |
---|---|---|---|
90%(1个9) | 36.5 天 | 2.4 小时 | 低可用 |
99%(2个9) | 3.65 天 | 14.4 分 | 基本可用 |
99.9%(3个9) | 8.76 小时 | 1.44 分 | 较高可用 |
99.99%(4个9) | 52.5 分钟 | 8.6 秒 | 高可用 |
99.999%(5个9) | 5.25 分钟 | 0.86 秒 | 极高可用 |
99.9999%(6个9) | 31.5 秒 | 86 毫秒 | 几乎完全可用 |
对于高并发系统来说,最基本的要求是:保证3个9或者4个9。原因很简单,如果你只能做到2个9,意味着有1%的故障时间,像一些大公司每年动辄千亿以上的GMV或者收入,1%就是10亿级别的业务影响。
但要想达到 3个9 或者更高等级是很困难的,这需要完整的监控运维体系和自动恢复能力,同时,还需要过硬的技术和大量的资金投入。
在高并发系统这个领域下,扩展性通常指的是,增加的硬件资源与增加的处理能力之间的关系。
计算公式:扩展性 = 增加的处理能力 / 增加的硬件资源 × 100%。
对于集群方式部署的系统来说,理想的扩展能力是:资源增加几倍,性能提升几倍。通常来说,扩展能力要维持在 70%以上。
实际工作中,可以作为将增加资源的便捷程度,以及所耗费的时间,作为系统扩展性的关键指标。 因为这两者可以体现系统在扩展性上的成熟度。
下面我们来看看几个常见的扩展维度。
扩展维度 | 描述 |
---|---|
垂直扩展性 | 硬件升降级时的耗时和可用性、性能提升幅度。 |
水平扩展性 | 增减节点时的耗时和可用性、性能提升幅度。 |
数据扩展性 | 数据存储的容量、数据读写的性能、数据分片和分区的灵活性以及数据一致性和可用性的维护程度。 |
服务扩展性 | 服务间的解耦程度、服务的独立部署和扩展性、服务的水平和垂直扩展能力等。 |
运维扩展性 | 系统的可监控性、可配置性、自动化运维的程度、故障处理和恢复的能力,以及系统的易用性和可维护性。 |
从高并发系统的整体架构角度来看,扩展的目标不仅仅是把服务设计成无状态就行了,因为当流量增加10倍,业务服务可以快速扩容10倍,但是数据库可能就成为了新的瓶颈。
像MySQL这种有状态的存储服务通常是扩展的技术难点,如果架构上没提前做好规划(垂直和水平拆分),就会涉及到大量数据的迁移。
因此,高扩展性需要考虑:服务集群、数据库、缓存和消息队列等中间件、负载均衡、带宽、依赖的第三方等,当并发达到某一个量级后,上述每个因素都可能成为扩展的瓶颈点。
了解了高并发设计的3大目标后,我们再来总结下通用的高并发设计方案。通用的设计方法主要是从「纵向」和「横向」两个维度出发,俗称高并发处理的两板斧:垂直扩展和水平扩展。
垂直扩展的目标,是提升单机的处理能力。主要方案如下:
1. 提升单机的硬件性能
主要通过增加内存、CPU核数、存储容量、或者将磁盘升级成SSD等堆硬件的方式来提升。
2. 提升单机的软件性能
使用缓存减少IO次数,使用并发或者异步的方式增加吞吐量。
水平扩展的目标,是通过集群部署进一步提高处理能力,因为单机性能总会存在极限。主要方案如下:
1. 做好分层架构
这是水平扩展的前提,因为高并发系统往往业务复杂,通过分层处理可以简化复杂问题,更容易做到水平扩展。
2. 各层水平扩展
无状态做水平扩容,有状态做分片路由。业务集群通常能设计成无状态的,而数据库和缓存往往是有状态的,因此需要设计分区键做好存储分片,当然也可以通过主从同步、读写分离的方案提升读性能。
分层 | 关键技术 |
---|---|
网络层 | - DNS: 实现地理级别的负载均衡,通常返回离用户距离比较近的IP。 - CDN: 解决用户网络访问时的“最后一公里”问题,即将内容缓存在离用户最近的地方,一般缓存的是静态资源,如图片、文件、CSS、Script脚本、静态网页等。 |
反向代理层 | - F5: 实现集群级别的负载均衡。 - Nginx / LVS:实现机器级别的负载均衡。 - Nginx 是软件的 7 层负载均衡,LVS 是内核的 4 层负载均衡,F5 是硬件的 4 层负载均衡。 - 软负载和硬负载的区别就在于性能,硬件远远高于软件;4 层和 7 层的区别就在于协议和灵活性。 |
业务服务层 | - 无状态: 应用通常被设计为无状态,那么应用就比较容易进行水平扩展。 - 拆分: 拆分的维度也比较灵活,需根据实际情况来选择。例如,根据系统维度、功能维度、读写维度、AOP 维度、模块维度、请求来源维度等等。 - 服务化: 拆分更多的是设计,服务化是落地,服务化一般都面临服务治理的问题。除了最基本的远程调用,还得考虑负载均衡、服务发现、服务隔离、服务限流、服务访问黑白名单等。甚至还有细节需要考虑,如超时时间、重试机制、服务路由、故障补偿等。 - 容器化: 容器化给运维带来了革命性的变化。基于Docker 打造自动化运维、智能化运维逐渐成为主流方式。容器化技术也天生适合当前流行的微服务,容器将微服务进程隔离到更小的实例里,结合容器编排技术,可以更方便快速地搭建服务高可用集群。 |
缓存层 | - 多级缓存: 在不同层级对数据进行缓存,来提升访问效率。 - 分布式缓存: 一般采用分片实现,即将数据分散到多个实例。 - 本地缓存: 将热点数据缓存到本地,降低分布式缓存的压力。 - 常见问题: 缓存穿透、缓存击穿、缓存雪崩、热key、大key等。 |
存储层 | - 读写分离: 分散读写操作的压力。 - 主机负责读写操作,从机只负责读操作,例如一主一从、一主多从。 - 分库分表: 分散数据存储的压力。 - 业务分库:按照业务维度将数据分散到不同的数据库服务器,突破单库引起的性能瓶颈。 - 垂直分表:将表中某些不常用且占了大量空间的列拆分出去,适合列很多且不常用的表。 - 水平分表:将表中数据按照某种路由算法切分到字表中,适合数据量特别大的表。 |
「简单总结一下」:高并发确实是一个复杂且系统性的问题。因此,在做高并发设计时,同样要秉承架构设计的3个原则:简单、合适和演进。“过早的优化是万恶之源”,不能脱离业务的实际情况,更不要过度设计,合适的方案就是最完美的。另外,如果业务场景不同,高并发的落地方案也会存在差异,但是总体的设计思路和可借鉴的方案基本类似。