
高并发系统设计的三大目标:高性能、高可用、可扩展
高并发指的是通过设计手段使系统能够处理更多的用户并发请求,承担更大的流量。它是所有架构设计的背景和前提,离开了它去谈论性能和可用性是毫无意义的。
在每秒一次请求和每秒一万次请求这两种不同的场景下,做到毫秒级响应时间和五个九(99.999%)的可用性,无论是设计难度还是方案的复杂度,都不是一个级别的。性能和可用性是我们在实现高并发系统设计时必须考虑的因素。
性能反映了系统的使用体验。例如,承担每秒一万次请求的两个系统,一个响应时间是毫秒级,另一个是秒级,它们给用户的体验肯定是不同的。可用性表示系统可以正常服务用户的时间。同样是承担每秒一万次请求的两个系统,一个可以全年不停机、无故障,另一个隔三差五宕机维护,作为用户,你会选择哪一个系统呢?显然答案是明显的。
另一个重要的因素是“可扩展性”。为什么呢?举个例子,流量分为平时流量和峰值流量两种,峰值流量可能是平时流量的几倍甚至几十倍。在应对峰值流量时,我们通常需要做更多的准备。易于扩展的系统能够在短时间内迅速完成扩容,更加平稳地承担峰值流量。
高性能、高可用和可扩展性是我们在设计高并发系统时追求的三个目标。接下来,我们将正式进入今天的话题:如何提升系统的性能?
性能优化原则
"天下武功,唯快不破”。性能是系统设计成功与否的关键,实现高性能也是对程序员个人能力的挑战。不过在了解实现高性能的方法之前,我们先明确一下性能优化的原则。
首先,性能优化一定不能盲目,一定是问题导向的。脱离了问题,盲目地提早优化会增加系统的复杂度,浪费开发人员的时间。某些优化可能会对业务有折中的考虑,从而损伤业务。
其次,性能优化也遵循“八二原则”,即你可以用20%的精力解决80%的性能问题。因此,在优化过程中一定要抓住主要矛盾,优先解决主要的性能瓶颈点。
再次,性能优化也要有数据支撑。在优化过程中,你要时刻了解你的优化让响应时间减少了多少,提升了多少的吞吐量。
最后,性能优化的过程是持续的。高并发的系统通常是业务逻辑相对复杂的系统,那么在这类系统中出现的性能问题通常也会有多方面的原因。因此,在做性能优化时要明确目标,持续寻找性能瓶颈,制定优化方案,直到达到目标为止。
在以上四个原则的指引下,掌握常见性能问题的排查方式和优化手段,就能在设计高并发系统时更加游刃有余。
性能的度量指标
一般来说,度量性能的指标是系统接口的响应时间,但是单次的响应时间是没有意义的,我们需要知道一段时间的性能情况。
因此,我们需要收集一段时间的响应时间数据,并依据一些统计方法计算出特征值,这些特征值能够代表这段时间的性能情况。常见的特征值有以下几类:
平均值:将这段时间所有请求的响应时间数据相加,再除以总请求数。平均值可以在一定程度上反应这段时间的性能,但它的敏感度比较差。因为如果这段时间有少量慢请求,平均值不能准确反映。
最大值:这段时间内所有请求响应时间的最长值。但它的问题在于过于敏感,一次异常的慢请求就能够使整体的最大值被拉高,不够准确。
分位值:比如90分位、95分位、75分位等。以90分位为例,我们把这段时间请求的响应时间从小到大排序,找出排在第90位的响应时间。分位值排除了偶发极慢请求对于数据的影响,更能反映这段时间的性能情况。

高并发下的性能优化
提高系统的处理核心数就是增加系统的并行处理能力,这个思路是优化性能最简单的途径。拿上一个例子来说,你可以把系统的处理核心数增加为两个,并且增加一个进程,让这两个进程跑在不同的核心上。这样从理论上,你系统的吞吐量可以增加一倍。当然了,在这种情况下,吞吐量和响应时间就不是倒数关系了,而是:吞吐量 = 并发进程数 / 响应时间。
计算机领域的阿姆达尔定律(Amdahl’s law)是吉恩·阿姆达尔在 1967 年提出的。它描述了并发进程数与响应时间之间的关系,含义是在固定负载下,并行计算的加速比,也就是并行化之后效率提升情况,可以用下面公式来表示:
(Ws + Wp) / (Ws + Wp/s)
其中,Ws 表示任务中的串行计算量,Wp 表示任务中的并行计算量,s 表示并行进程数。从这个公式我们可以推导出另外一个公式:
1/(1-p+p/s)其中,s 还是表示并行进程数,p 表示任务中并行部分的占比。
当 p 为 1 时,也就是完全并行时,加速比与并行进程数相等;当 p 为 0 时,即完全串行时,加速比为 1,也就是说完全无加速;当 s 趋近于无穷大的时候,加速比就等于 1/(1-p),你可以看到它完全和 p 成正比。特别是,当 p 为 1 时,加速比趋近于无穷大。
我们似乎找到了解决问题的银弹,是不是无限制地增加处理核心数就能无限制地提升性能,从而提升系统处理高并发的能力呢?很遗憾,随着并发进程数的增加,并行的任务对于系统资源的争抢也会愈发严重。在某一个临界点上继续增加并发进程数,反而会造成系统性能的下降,这就是性能测试中的拐点模型。
减少单次任务响应时间
要减少任务的响应时间,首先需要确定系统是CPU密集型还是IO密集型,因为不同类型的系统性能优化方式不同。
在CPU密集型系统中,主要任务是处理大量的CPU运算。为了优化这类系统的性能,可以采用更高效的算法或减少运算次数。例如,如果系统的主要任务是计算Hash值,选择更高性能的Hash算法可以显著提升系统性能。发现这类问题的主要方式是通过一些Profile工具来找到消耗CPU时间最多的方法或模块,比如Linux的perf、eBPF等。
在IO密集型系统中,大部分操作是在等待IO完成,其中IO包括磁盘IO和网络IO。常见的IO密集型系统包括数据库系统、缓存系统和Web系统。优化这类系统的性能,可以采用两种主要手段:
使用工具:Linux提供了丰富的工具集,可以满足性能优化的需求,包括网络协议栈、网卡、磁盘、文件系统、内存等。这些工具的使用方法多种多样,可以在排查问题的过程中逐渐积累经验。此外,一些开发语言还提供了针对语言特性的分析工具,例如Java语言的内存分析工具。
监控:通过监控系统,可以对任务的每一个步骤进行分时统计,从而找到任务中哪一步消耗了更多的时间。找到系统的性能瓶颈后,优化方案会因问题不同而不同。例如,如果是数据库访问慢,就需要检查是否存在锁表情况、是否存在全表扫描、索引是否合适、是否需要JOIN操作、是否需要加缓存等。如果是网络问题,就需要检查网络参数是否有优化空间,抓包查看是否有大量的超时重传,网卡是否有大量丢包等。

