“应用程序可扩展吗?” 经理问。每周的系统路线图会议几乎进行了一半。我以为我们已经完成了所有技术部分,很快就会转向与业务相关的问题。
我团队的架构师清了清嗓子回答说:“它可以轻松处理两倍于通常的负载。”
经理看了很久,一副不相信这个答案的样子。但是,在没有任何更好的观点的情况下,他继续进行进一步的更新。我含蓄地相信我们的系统确实是可扩展的。毕竟,建筑师已经明确地宣布过了。
一年下来,我知道了真相。一个晴天,系统因负载过大而崩溃。
该银行为假期推出了一种新的贷款产品。商业战略得到了回报,需求激增。由于我们的应用程序提供了做出信用决策的核心数据,因此它被来自其他几个应用程序的传入请求淹没了 5 倍。
我们添加了更多 CPU 来处理我们服务的负载。同样的策略在最近几次都奏效了。然而,这次没有。
我们的系统是可扩展的,直到它不再是。
经过分析,我们发现问题并不完全出在我们的应用程序代码中。我们的应用程序由与银行中央数据库相连的多项服务组成。与数据库的连接是使用连接池建立的。
我们的错误是我们对打开的连接数粗心大意。事实证明,数据库管理不是。
DBA 对我们应用程序组的打开连接数有一个严格的限制。当对我们应用程序的传入请求激增时,我们最终获得了更多的连接,直到我们碰壁。无论我们添加多少额外的 CPU 来增加应用程序容量,都没有任何区别。
雪上加霜的是,DBA 拒绝增加允许的连接数。剩下的唯一选择是对我们的应用程序进行代价高昂的更改以正确处理连接。出于所有实际目的,我们的系统不再具有可扩展性。
当然,这是我们的错误。我们应该考虑到有限的连接数并明智地使用它们。尽管如此,问题并非无法解决。但这种改变并不划算。
虽然情况最终得到了处理,但它激起了我对这个主题的兴趣。我们认为该系统是可扩展的。但事实并非如此。至少在我们达到连接限制之前不会。
很明显,我们对可伸缩性的真正含义的理解是错误的。我决定深入了解整个情况。我最初的动机是避免陷入与我团队的架构师相同的境地
可扩展性是构建分布式系统的基本挑战之一。
在研究分布式系统的可伸缩性时,我遇到的最常见的定义如下:
可伸缩性是系统处理增加的工作负载的能力。
这个定义在现实生活中如何应用?
活跃使用的系统的开发人员监控其工作负载水平。他们创建流程来预测系统性能何时会变得不尽如人意。目标是在达到危险级别之前增加系统的容量。
在这种可扩展性方法中,主要关注点是确定一个需求区间,在该区间内系统将以可接受的水平执行。如果这个间隔足够大,则系统被认为是可扩展的。
在我看来,这个定义非常有限。此定义的重点始终是工作负载。
该定义的主要关注点是,如果系统随着工作负载的增长继续充分执行,则该系统是可扩展的。没有注意必须如何修改系统才能将性能保持在同一水平。
正如我们在案例中发现的那样,我们的系统本可以继续保持良好的性能。但是数据库端所需的更改是不可接受的。更改代码也不符合成本效益。
通用的可伸缩性定义不关注容量增加的方法或其对系统的整体影响。我们不会问重要的问题,例如:
为了捍卫定义,它是系统设计人员和开发人员使用的最常见的定义。我们总是听到有人说系统是可扩展的,它可以毫无问题地处理双倍的工作负载。
在寻求了解可伸缩性的过程中,我遇到了另一种经常被忽视的可伸缩性定义。但是,我开始相信它在实际情况中可能更为重要。
可伸缩性是通过重复应用具有成本效益的策略来扩展系统容量来处理增加的工作负载的能力。
重点立即转移到增加容量的策略上。
我们不再对一次性增加容量以提高可扩展性感兴趣。我们感兴趣的是增加容量的策略以及可以经济高效地应用该策略的次数。
这种思路总是让我想起像拉斐尔纳达尔或罗杰费德勒这样的世界级网球运动员。多年来,像纳达尔或费德勒这样的球员有多少次调整他们的比赛风格以适应不断变化的比赛需求?
很多次,我想。
他们所做的每一次改变都延长了他们的职业生涯并使他们更加成功。
然而,做出改变并不容易。许多玩家升到顶峰后就因为无法适应而失败。即使是最好的球员也必然会在尝试适应时遇到困难。球员越容易做出改变,他在比赛中保持统治地位的可能性就越大。
扩展系统也是如此。
当我们考虑可伸缩性的第二个定义时,我们开始考虑关于我们系统的其他类型的争论。
专注于扩展策略的重复应用可以让我们更加了解我们的选择。
例如,将O(n^2)替换为O(nlogn)算法可以在相同的时间内处理更大的工作负载。换句话说,用更高效的算法替换算法可以提高系统的可扩展性。
但是我们可以重复使用这种方法吗?我不这么认为。
一旦我们有了最有效的算法,算法替换策略就不再可行了。您不能不断地应用该策略来提高系统的可扩展性。
有了可伸缩性的两个定义,我终于能够理解这个基本问题。这是我们会议中突然出现的同一个问题,但没有得到充分回答。
为了回答这个问题,我们最终将系统标记为可扩展或不可扩展。在我看来,这是过于简单化了。
大多数系统在某种意义上都可以扩展。但是,没有任何系统可以无限扩展。
那么我们应该如何处理可扩展性呢?
比起给系统贴标签,比较两个不同系统的可扩展性更有收获。考虑下图,它显示了两个假设系统 A 和 B 的响应与需求曲线。
对于任何给定的需求水平,与系统 B 相比,系统 A 的响应更差。如果存在响应的最大容忍值,系统 A 将比系统 B 更早达到该值。
系统 B 比系统 A 更具可扩展性。
当然,如果两条线继续以相同的单调速率上升,它们最终将达到对资源的需求超过其可用性的点。到那时,两个系统的响应时间都将变得不尽如人意。尽管 A 和 B 的分数可能不同,但它们表示系统可扩展性的限制。
请记住 - 没有任何系统可以无限扩展。
系统不遵循响应与需求指标的单调增长率。曲线看起来更像下面的例子。
假设的系统可以很好地容忍需求增加,直到它达到一个重要的设计限制。这种限制在响应需求曲线中形成了类似膝盖的形状。在超过特定需求水平后,响应指标就会失控。
设计师的目标是使曲线的拐点尽可能靠右。一旦系统接近膝盖,它就不再具有可扩展性。需求的任何增加都会将其推下山坡。
可扩展性的理论方面让我大开眼界。但我还是觉得少了什么。
我无法将上述图表与我们的系统完全联系起来。每当负载普遍增加时,我们都能够在最初几天成功地扩展我们的系统。只有当我们达到数据库限制时,事情才会变得糟糕。
这种行为不符合描述可伸缩性的两个理论模型中的任何一个。
大多数真实系统表现出更多的混合行为。下图显示了这一点:
当系统在底部绿色区域运行时,它响应良好。当它开始在中间区域徘徊(以黄色显示)时,响应开始变得不可接受。当它最终进入顶部区域(以红色显示)时,系统变得不可用。
在我看来,这是对可扩展性的第二个定义的更准确的表述。
上述事件也描述了我们系统的情况。尽管我们能够在一段时间内保持系统的可扩展性,但它最终达到了一个临界点,超过这个临界点,任何具有成本效益的解决方案都不可能实现。
那么,系统是否可扩展?
答案是——视情况而定!而不是以一种逃避的方式。
可扩展性更像是一个不断发展的移动目标,而不是一个固定的状态。
如果系统所有者有能力继续投入资金以满足更高的需求水平,则系统可以扩展到一定程度。除此之外,没有任何具有成本效益的行动可以缓解响应指标问题。
如果系统所有者在一开始就没有钱购买额外的资源,那么系统是不可扩展的。
无论如何,没有任何系统是可以无限扩展的。
我理解可伸缩性真正含义的旅程得出了一些重要的结论。
在我看来,您不能简单地在系统上贴上可扩展性的标签,然后就此结束。
所有系统在某种程度上都是可扩展的。但这并不意味着您可以以具有成本效益的方式无限期地继续扩展它们。
理想的可扩展性情况需要采用细致入微的系统设计方法。只关注增加资源以提高可扩展性是一个陷阱。您还需要考虑额外资源的成本效益。
通过这篇文章,我的目标是将这种观点带入讨论。
请在下面的评论部分分享您的想法。