- 高并发系统:设计原理与实践
- 唐扬
- 3500字
- 2025-04-24 19:43:54
1.2 高并发系统的设计难点
在Web 2.0时代,涌现出了许多拥有海量用户的应用,它们所承受的并发流量是Web 1.0时代的网站无法想象的。如此大的并发流量,给系统带来的技术挑战是前所未有的。对承受如此大并发流量的高并发系统来说,系统设计的难点主要有两个:系统的高可用和访问的高性能。
1.2.1 系统的高可用
在维基百科中,高可用(high availability,HA)的定义是系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。系统无中断地运行其功能的时间越长,代表系统的高可用越好。在很多开源组件中,都有保证高可用的方案。
例如,Redis的官方文档中专门有一部分用来介绍Redis的高可用方案——哨兵(sentinel),该方案的示意如图1-1所示。哨兵节点是一组独立部署的进程,每个进程会定期向Redis主节点和从节点发送命令,探测它们是否存活。如果主节点没有正确地响应命令,那么哨兵节点就会触发Redis集群的主从切换,重新为Redis集群指定一个能够正常运行的从节点作为主节点。

图1-1 哨兵高可用方案的示意
再如,在Hadoop 1.0中,用来存储集群元信息的NameNode只有一个,没有提供高可用方案。NameNode相当于整个Hadoop集群的心脏,一旦发生故障对整体系统的影响非常大,因此Hadoop 2.0提供了NameNode的高可用方案,即系统中除了存在一个正常运行的NameNode(active NameNode),还存在一个备用NameNode(standby NameNode)。正常运行的NameNode和备用NameNode之间可以通过共享存储文件来共享数据。当正常运行的NameNode发生故障时,备用NameNode会自动代替故障的NameNode,成为新的正常运行的NameNode提供给集群使用。
对于任何系统来说,保证系统的高可用是一名开发人员的首要任务,而在现实的系统运维过程中,影响系统可用性的原因有很多。下面就介绍几个重要的原因。
影响系统可用性的一个原因是系统可能随时发生灾难性的“黑天鹅”事件,这对整体系统可用性的影响是巨大的。庞大的系统中存在海量的服务和资源,这些服务和资源之间的关系错综复杂。即使开发和运维人员在保障系统高可用方面采取了多种措施,也很难做到尽善尽美。一个小的疏漏可能就会导致系统的全盘崩溃。
例如,2022年12月18日,阿里云香港Region的可用区C出现了一次近14个小时的故障[],故障的原因简单来说就是机房里的制冷系统出现了问题,机房里4套主备制冷系统共用了一套水路循环系统,导致制冷系统无法做到主备切换,必须花3个多小时等待制冷设备供应商来现场处理;供应商来了之后,又花了2个多小时对制冷系统进行了补水排气,结果在尝试重启制冷设备的时候,发现制冷系统存在一个群控的逻辑,导致无法重启单台制冷设备。于是,供应商又花了3个多小时来解除这个群控逻辑,并且对全部4台制冷设备进行了重启。在故障发生了近11个小时之后,机房的温度终于趋于稳定了。
[1] 此案例于2024年9月20日引自阿里云发布的“关于阿里云香港Region可用区C服务中断事件的说明”。
在整个处理过程中还发生了一个插曲,就是在整体机房温度没有恢复正常的时候,机房的一个包间竟然触发了强制消防喷淋,所以在机房温度恢复正常之后,阿里云没有对这个包间的服务器直接上电,担心服务器进水导致数据丢失,因此阿里云又不得不对触发强制消防喷淋的包间内的服务器做了数据完整性校验,这个过程又延续了3个多小时。
总结这个案例可以得出,无论是设计系统还是设计系统故障时的应急响应都需要注意以下3个关键点。
● 不能忽视系统中的单点问题。例如,制冷系统存在单点问题,虽然有主备制冷系统,但是它们共用一套水路循环系统,一旦水路循环系统发生故障,主备制冷系统都不能正常工作。
● 做好预案,且要经过完善的故障演练。预案不足或预案没有经过完善的故障演练,可能导致切换备用制冷系统不成功;又因存在群控逻辑而无法重启单台制冷设备,需要由供应商现场修改配置来解除群控逻辑,导致故障的解决时间延长了几个小时。对于一些互联网系统,尤其是“头部”互联网公司的核心系统,系统的高可用至关重要,即使出现一个小时的故障都可能带来不可估量的损失。
● 高可用策略不能只是理论上的高可用,需要能在实践中真正发挥作用。2021年,我维护的一个系统就出现了“只是理论上的高可用”的问题。当时,我维护的系统已经在云上做了多可用区部署,保证发生故障时可以把系统流量从一个可用区切换到另一个可用区,但是因为部署流量切换系统、监控系统、报警系统等多个运维系统的可用区恰好发生了故障,导致高可用策略失效。
当然,“黑天鹅”事件发生的概率非常小,可一旦发生,对于系统来说就是致命的灾难。然而无独有偶,在2023年,国内因为机房制冷系统故障导致的系统长时间故障又接连发生了2起。此类故障不仅会给公司带来巨大的经济损失,还会严重影响用户体验和伤害公司信誉等。因此,排除故障隐患、演练故障预案,是降低“黑天鹅”事件影响的必做之事。
影响系统可用性的另一个原因是突发流量。突发流量的典型载体是以微博为代表的社交媒体,它们经常会因突发流量而出现不可用的问题。大型的社交媒体往往拥有海量的用户,极大地加快了信息流动的速度。用户可以非常便捷地在社交媒体上获取信息,一些热点事件也更容易在社交媒体上发酵,短时间内带来大量的用户访问请求,给系统带来极大的冲击,影响系统的可用性。
影响系统可用性的更重要原因是系统变更。《SRE:Google运维解密》一书中提到:SRE的经验告诉我们,大概70%的生产事故由某种部署的变更而触发。这里提到的变更不仅仅是代码的变更,也包括数据的变更和配置的变更等。因此,开发团队需要关注变更的流程,如规定合适的变更时间、变更前准备必要的检查清单等,这部分内容将在6.5.1节详细介绍。
1.2.2 访问的高性能
系统性能是影响用户体验的直接因素,谷歌针对网页的用户体验提出了一个指标——与下一次绘制的交互(interaction to next paint,INP),它会在网页的生命周期中记录用户与网页所有交互的延迟时间(包含输入延迟、处理延迟和展示延迟),以度量网页对用户交互的总体响应速度。低INP意味着网页能够可靠地响应用户交互。INP分为以下3个等级。
● INP小于等于200 ms:表明网页的响应速度良好,用户基本感知不到网页的刷新和渲染。
● INP大于200 ms但是小于等于500 ms:表明网页响应速度有一定的优化空间。
● INP大于500 ms:表明网页响应性差,用户体验也比较差,需要进行网页加载优化。
优化INP的方式是多种多样的,如减少请求服务端次数、优化接口响应时间、延迟加载、使用页面缓存等。在服务端优化INP是非常直接的方式之一,而优化接口响应时间一般有以下两个思路:
● 提高系统的并行能力,让系统在单位时间内能够处理更多请求;
● 缩短单次请求的响应时间。
下面分别介绍这两个思路。
1.提高系统的并行能力
阿姆达尔定律(Amdahl’s law)由吉恩·阿姆达尔(Gene Amdahl)在1967年提出,是描述计算机系统设计的重要定量原理之一。在高并发系统设计中,阿姆达尔定律描述的是在固定负载下,并发进程数与响应时间之间的关系。
在固定负载下,并行计算加速比(即进行并行化处理后,系统处理任务的效率提升情况)可以定义为R,R越大表示并行化处理后效率提升越明显,即系统响应时间越短;R越小表示并行化处理后效率提升越不明显,即系统响应时间越长。R可以用式(1.1)表示:
(1.1)
其中,表示任务中串行进程的计算量,
表示任务中并行进程的计算量,n表示并行化处理的节点个数。从式(1.1)可以推导出式(1.2):
(1.2)
其中,n表示并行化处理的节点个数,p表示并行任务数占总体任务数的比例。
当p=1时,即任务全部是并行任务时,R=n;当p=0时,即任务全部是串行任务时,R=1,也就是完全无加速;当n趋近于无穷大时,也就是拥有无限个并行化处理节点时,R=1/(1−p),此时R和p正相关,即并行任务数占总体任务数的比例越高,R越大。
2.缩短单次请求的响应时间
想要缩短单次请求的响应时间,首先要判断系统是CPU密集型还是I/O密集型的,因为优化不同类型的系统性能的方式不尽相同。
CPU密集型系统需要处理大量的CPU运算,因此选用更高效的算法或者减少CPU运算次数就是这类系统性能的优化方式。例如,如果系统的主要任务是计算哈希值,那么这时选用更高效的哈希算法就可以大大提升系统的性能。发现CPU过载问题的主要方式,是通过一些CPU剖析工具(如针对Linux系统的perf、eBPF、gperftools等)来找到消耗CPU时间最多的方法或者模块。
I/O密集型系统的主要特点是系统中请求的大部分时间都用在等待I/O读写操作完成。当前大多数互联网系统都属于I/O密集型系统。如果观察这类系统的运行指标,就会发现系统中计算资源的CPU使用率的峰值基本上在40%~50%,这就是因为系统中请求的大部分时间都用在等待I/O读写操作完成了,如等待数据库、缓存中的数据返回,或者等待依赖的其他服务的请求返回,CPU反倒处于相对空闲的状态。这类系统的优化方式多是减少I/O读写操作的次数和耗时,如优化数据库的慢查询,使用速度更快、性能更好的缓存来减少对数据库的请求,使用连接池来减少连接的频繁创建,等等。