1.4 可扩展性系统设计基本原则
扩展系统的基本目标是在应用程序的某些特定维度上增加容量。一个常见的维度是增加系统在给定时间段内可以处理的请求数量。这称为系统的吞吐量。让我们用一个类比来探索我们在扩展系统和增加吞吐量时可以利用的两个基本原则:复制和优化。
1932年,世界标志性工程奇迹之一——悉尼海港大桥(https://oreil.ly/u7bOH)启用。现在,一个相当安全的假设是,2021年的交通流量略高于1932年。如果你有机会在过去30年的高峰时间开车过桥,那么你就会知道每天的交通流量大大超出它的容量。所以,我们如何提高物理基础设施(例如网桥)的吞吐量呢?
这个问题在20世纪80年代的悉尼变得非常突出,当时人们意识到必须增加海港过境的容量。解决方案是增加了名声略小的悉尼海港隧道(https://oreil.ly/1VWm7),它在海港下方基本遵循相同的路线。它提供了四个额外的车道,因此增加了大约三分之一的港口过境能力。在不太远的奥克兰[8],海港大桥(https://oreil.ly/E7yJz)也存在容量问题。它建于1959年,只有四条车道。本质上,奥克兰采用了与悉尼相同的解决方案,即增加容量。但是,奥克兰没有建造隧道,而是通过拓宽桥的两边,巧妙地将桥上的车道数量翻倍,并用有趣的名字“Nippon clip-ons”[9]来命名该桥。
这些例子说明了我们在软件系统中增加容量的第一个策略。我们基本上复制了软件中处理请求的资源,以提供更多的请求处理能力,从而提高吞吐量,如图1-1所示。复制的资源类似于桥梁上的车道,为到达的请求流量提供了一个几乎独立的处理路径。
幸运的是,在基于云的软件系统中,只需点击鼠标即可实现复制,我们可以有效地复制用来处理请求的资源,高达数千倍。在这方面,我们比桥梁建设者容易得多。尽管如此,我们仍需要保证“好钢用在刀刃上”,确保复制的资源会切实缓解实际的瓶颈。向未被淹没的路径添加容量将增加不必要的成本,也不会提供可扩展性优势。
图1-1:通过复制资源来增加容量
第二种可扩展性策略也可以通过桥的示例来说明。在悉尼,一些细心人士意识到,早上有更多的车辆从北向南过桥,而在下午则看到了相反的模式。因此想出了一个聪明的解决方案——在早上将更多的车道分配给高需求的方向,并在下午的某个时间进行切换。这在不分配任何新资源的情况下有效地增加了桥梁的容量——我们优化了(optimize)已有的可用资源。
我们可以在软件中遵循同样的方法来扩展系统。如果我们通过以下方法可以优化请求的处理,那么可以在不增加资源的情况下提高系统能力:使用更有效的算法;在数据库中添加额外的索引以加快查询速度;用更快的编程语言重写服务器。典型的例子是Facebook创建的用于PHP的HipHop(已停止维护;https://oreil.ly/d2JFX),HipHop将PHP代码编译为C++,将Facebook的网页生成速度提高了6倍。
贯穿本书,我将重新审视这两个设计原则,即复制和优化。由于我们正在构建的是分布式系统,你将看到采用这两个设计原则会产生许多复杂的影响。分布式系统具有很多特性,使得构建可扩展系统变得有意思,在这里,“有意思”既有积极的含义,也有消极的含义。