You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
服务器的理想 CPU 使用率是多少呢?当我们查看一个设计精良、运行顺畅的服务监控面板时,平均一两天内,我们希望看到的 CPU 使用率应该是多少?
这个问题很普遍,而且也没有一个固定的答案。长期以来,我一直认为 CPU 使用率越高越好:我们应该力求让它接近 100%。原因何在?因为低于 100%的使用率意味着有硬件资源没有被充分利用,也就是我们在浪费资源。如果服务没有充分利用其 CPU,我们可以考虑把它迁移到更小的服务器上,或者在同一台服务器上增加额外的工作负载。
然而,这种简单的想法并不总是正确的。
设想一下,如果我们的服务运行在接近 100%的 CPU 使用率,突然我们的服务意外爆红,接收到了大量的流量怎么办?或者如果我们想部署一个新功能,它在每次请求上都需要额外的 CPU 资源呢?
如果我们的服务已经运行在 100%的 CPU 使用率,再增加负载就会遇到问题。运行在 100%的使用率没有留下任何余地来应对突增的负载。这种情况下,我们可能不得不降低服务质量,紧急增加服务器容量,或者两者同时发生。
这个例子展示了一个普遍现象:提高效率往往会牺牲系统的弹性。我们对系统进行的优化越深入,这种取舍往往越明显。
当我们过分追求系统的高效率,往往就会牺牲它的弹性。反过来,增强系统的鲁棒性往往会在短期内降低其效率。当然,并非没有双赢的情况;有时,我们可以通过改进来推动Pareto 前沿的发展;例如,修复一些明显的性能漏洞就能达到这个效果。但是,一旦付出的努力超过了一定程度,就不得不做出选择。
在这里提到的“弹性”,是一个比“可靠性”或“正常运行的能力”更广泛的概念。它指的是系统对各种变化(包括软件错误、故障、产品需求变化、市场变化、组织或团队构成变化等)的适应和响应能力。
权衡的例子
这种权衡发生在容量规划以外的领域。它几乎适用于任何技术系统或组织的每个层面。以下是我在其他一些地方观察到的情况:
冗余
通常,我们会运行多个服务实例,并配置负载均衡器。这样,如果某个实例出现故障,系统会自动将工作负载转移到其他实例上。在一些高度发达的组织中,这种做法甚至可以应用到整个数据中心,构建一种能够应对整个数据中心故障的架构,把工作负载转移到其他数据中心。
为了实现这种冗余,每个实例都必须预留足够的空闲容量,以便在出现故障时承接其他实例的额外负载。在正常运行状态下,这些预留容量通常处于闲置,或者用于执行一些低优先级的任务,这些任务可以在需要时立即中断。我们期望的冗余级别越高,就需要在正常运行状态下预留越多的空闲容量。
优化
高级的性能优化通常是通过充分利用特定问题领域的特性或结构来实现的。将某些固定规则(不变量)整合到数据结构和代码组织中,通常能显著提升性能。但是,如果过分依赖这些特定的假设,要改变它们就会变得更加困难,这也导致高度优化的代码变得难以更新或增加新功能。
以我对 Sorbet 的反思为例,我谈到了我们决定仅在本地进行单次类型推断,并将这一假设深植于我们的代码和数据结构之中。这一选择确实大幅提高了效率,但也在一定程度上增加了系统的脆弱性:因为这些假设,Sorbet 代码库中很难或几乎不可能实现一些其他类型系统所具有的功能。虽然我依然认为这对那个项目来说是正确的选择,但这些权衡是值得被认真考虑的。
Hillel Wayne 在他的文章中提到了一个类似的概念,他称之为“聪明代码”。定义为:
利用对问题深入了解的代码。
他也指出,虽然聪明代码通常效率很高,但有时也可能变得很脆弱。
序列化格式
很难找到比“直接将内存中的结构体(
structs
)数据复制到磁盘”更高效的数据序列化(将数据转换为可存储或传输格式的过程)方法。这种方式几乎不需要编写任何额外代码,且在保存或读取数据时几乎没有额外的序列化成本。但这种方法也使系统变得脆弱——哪怕是添加一个新字段,也可能需要重写整个数据或采取一些特别的处理措施。此外,在不同字节顺序或字节大小的计算机之间共享数据也会成为一项挑战。
另一方面,如果我们选择将所有数据以 JSON 或类似的通用格式存储,这将为添加新字段或不同模块间的通信提供极大的灵活性,且不会影响现有代码。然而,这样做的代价是在数据传输和 CPU 处理数据序列化与反序列化时会有显著的额外开销。
分布式系统
我特别喜欢的一篇系统论文是 COST。这篇论文研究了许多大数据平台,并发现这些平台的一个共同特点是随着硬件资源的增加,它们的处理能力也几乎线性增长,但这种扩展性的代价是,与经过优化的单线程程序相比,它们的效率要低得多。
我发现这是一个普遍存在的权衡问题。分布式计算框架能够灵活且稳定地通过增加资源来处理几乎任何规模的工作负载。它们可以通过增加计算资源来处理效率低下的代码,并且能够在硬件出现故障时保持运行不受影响。需要处理更多数据吗?只需添加更多硬件(通常这是自动完成的,比如通过自动扩展技术)。
而另一方面,精心编写的单节点解决方案虽然通常运行速度更快(有时快 10 到 100 倍),但它们也更加脆弱。比如,如果数据量增长到无法在单个节点上处理,或者需要执行成本更高的分析,或者团队中的新工程师无意中在代码的关键部分编写了效率低下的代码,这些都可能导致整个系统崩溃或无法完成既定任务。
小型团队与大型组织
小团队——甚至是单人团队——可以极其高效和富有成效。团队规模越小,沟通的成本就越低,每位工程师更容易保持对项目的深入理解。这样的团队写文档的需求较少,关于变更的沟通也更少,新成员的培训和适应时间也减少了。小团队通过策略如“深思熟虑且努力尝试”,往往可以做得更多,而且通常不需要过度依赖像代码审查工具这样的辅助工具。
在合适的情况下,如果有精心设计和经验丰富的工程师,小团队有时能够达到一个大团队(大约是他们的 10 倍规模)的产出水平——这无疑是效率的巨大提升!
然而,小团队也更脆弱,对组织变化、技术领域的变动或项目的业务需求变化的适应性较弱。比如,在一个四人团队中,如果有一人离职,整个团队的工作效率就会下降 25%。更严重的是,这样的团队往往缺乏招聘和培训新成员的经验,大量的知识和项目文档可能只存在于剩下成员的记忆中。
同样地,如果业务重心发生变化或需要开发新产品功能,这些变化可能迅速超出团队的处理能力。在这种情况下,快速扩大团队规模同样会面临挑战。
自动化与人工流程
通常,机器执行任务比人工操作更高效——成本更低,速度更快,而且往往更可靠。
然而,人类具有极强的适应性,相比之下,机器(无论是物理机器还是软件系统)更加脆弱,行为模式更加固定。纳入人类参与的系统能够更灵活地应对环境变化或意外事件。
即便是保留所有现有人员并通过自动化手段提升他们的工作效率,我们也可能面临自动化依赖的问题。在这种情况下,人类可能过分依赖自动化,不适当地信任它,或者失去了在没有自动化的情况下独立操作的能力,以至于在紧急情况下无法恰当地手动干预。
松弛
这些观察实际上与松弛(不是指软件产品 Slack)有关。
具有适量松弛的系统——至少从短期和简单的分析角度来看——按定义是效率较低的:那些松弛是处于“闲置”状态的时间或资源,原本可以用于产出。
然而,从更宏观的角度来看,这种松弛对于保持系统的弹性至关重要:系统中的“余地”使其能够应对小幅度的干扰或小错误;开发者或操作员可以利用这种松弛来应对突发负载或解决根本问题,防止它们演变成大灾难或对外界可见。
一个没有松弛的系统在正常情况下效率很高,但一旦遇到改变其常规操作模式的情况,就会显得脆弱,并且很快崩溃。
总结
我尝试举了一些具体的例子,展示了效率和可靠性之间的对立,以及它们是如何在相反的方向上施加压力或进行权衡的。我希望已经让你相信,这是一个普遍现象,即使在可能还没有明显权衡的情况下,这两个价值观也往往是相互对立的,并会导向不同的决策方向。
不幸的是,仅凭这个观察并不能告诉我们如何处理任何特定的系统。如果我们在分析某个系统时发现它的输入利用效率不高,我们无法立即判断这是由于功能紊乱和糟糕的设计选择造成的,还是这种表面上的低效率其实支持着巨大的冗余、灵活性和松弛,这将使系统能够应对任何可能的变化。我们需要更深入地了解,并且几乎总是需要特定领域的专业知识。
此外,有时确实存在所谓的免费午餐。某些设计选择或决策可以将 Pareto 前沿向外推进,而不仅仅是在它上面移动。这些选择在经过良好优化的系统中可能很少见,但我们不能忽视它们的可能性。并且,许多系统还没有得到充分优化!
另外,根据系统的不同,设计空间中的最佳点也会有所不同。有时,可靠性或弹性至关重要,我们正确地容忍了显著的初级低效率。然而,在其他情况下,极端的效率可能是正确的目标:也许我们的利润空间非常有限,这是我们唯一的选择,或者我们对我们的领域和系统需求的稳定性有足够的信心,相信我们不会面临任何剧烈的变化。
因此,我所能做的主要是呼吁我们,作为工程师、设计师和系统观察者,在工作中带着对这种权衡及其含义的意识。当我们指责某个系统浪费和低效时,我们应该暂停一下,思考这种“浪费”可能带来的好处。当我们着手优化一个系统时,我们应该停下来理解当前系统的灵活性所在,哪些部分至关重要,并尽可能保留这些部分。当我们为系统、团队或组织设定效率目标时,我们应该意识到,如果没有相反的压力,我们可能也在让系统变得更脆弱和易碎。
Beta Was this translation helpful? Give feedback.
All reactions