很多架构决策和性能有关。对于大多数与性能相关的问题,我的办法是首先建立系统,运行它并进行测量,然后基于测量有纪律地优化。但是,有一些架构上的决策对性能的影响,可能是后期优化难以弥补的。而且即使这种影响可以在后期很容易地弥补,参与这个项目的人仍然会从一开始就担心这些决策。
在这样的一本书中讨论性能通常很困难。这是因为任何关于性能的建议,在没有在你的配置上进行测量之前都不应该被视为事实。我也经常看到一些设计方案因为性能方面的考虑而被接受或拒绝,但是一旦有人在应用所使用的真实环境中做一些测量,就会证明这些考虑是错误的。
本书中将提出一些这方面的建议,包括尽量减少远程调用(它在很长时间内都被认为是很好的性能建议)。尽管如此,你还是应该通过在你的应用上测量来验证每一条建议。同样,本书中的示例代码也有一些地方为了提高可读性而牺牲了性能。在你的系统中,需要自行决定是否进行优化。在做性能优化后,一定要与优化前进行测量对比,以确定真的得到了优化,否则,你可能只是破坏了代码的可读性。
还有一个很重要的推论:配置上的重大变化会使得任何关于性能的事实失效。因此,在升级虚拟机、硬件、数据库或其他东西到新的版本时,必须重新进行性能优化,并确保它们仍然有效。很多情况下,配置变更可能会改变一些事情,有时候你真的会发现,以前为了提升性能做的优化,在新环境下居然损害性能。
关于性能的另一个问题是很多术语的使用不一致。最显著的受害者是“可伸缩性”(scalability),它可能有6~7种含义。下面是我所使用的术语。
响应时间 是系统完成一次外部请求处理所需的时间。这些外部请求可能是用户交互行为,例如按下一个按钮,或是服务器API的调用。
响应性 是指系统响应请求的速度,而不是处理请求的速度。这个指标在许多系统里非常重要,因为对于一些系统而言,如果其响应性太慢,用户将难以忍受——尽管其响应时间可能不慢。如果在请求处理期间,系统一直处于等待状态,则系统的响应性和响应时间是相同的。然而,如果能够在处理真正完成之前就给用户一些信息表明系统已经接到请求,则响应性就会好一些。例如,在文件拷贝过程中,为用户提供一个“进展条”,将会提高用户界面的响应性,但并不会改善响应时间。
延迟 (Latency)是获得系统任何形式响应的最小时间,即使应该做的工作并不存在。通常它是远程系统中的大问题。假设我们让程序什么都不做,只是在什么都不做时告诉我,则如果在本机上运行程序,一般都会立即得到响应。但是,如果在远程计算机上运行程序,由于请求和响应的传输时间,可能需要几秒钟才能得到响应。作为应用开发者,我经常对延迟无能为力。这也是要尽量避免远程调用的原因。
吞吐率 是给定时间内能够处理多大的请求量。如果考察的是文件拷贝,则吞吐率可以用每秒字节量来表示。对于企业应用来说,吞吐率通常用每秒事务数(tps)来度量。这种方法的一个问题是指标依赖于事务的复杂程度。对于特定系统,应该选取普通的事务集合。
在这里, 性能 或者指吞吐率,或者指响应时间,取决于哪个对你更重要。当通过某种优化技术后,使得系统的吞吐率提高了,但是响应时间下降了,这时就不好说“性能”了,最好用更准确的术语表示。从用户角度而言,响应性可能比响应时间更重要,因此,为了提高响应性而损失一些响应时间或者吞吐率,在用户看来性能是提高了。
负载 是指系统承受的压力有多大,可以用当前有多少用户与系统相连来表示。负载有时也作为其他指标(如响应时间)的背景。因此,我们可以说:在10个用户的情况下,请求响应时间是0.5秒,在20个用户的情况下,请求响应时间是2秒。
负载敏感度 是指响应时间随负载变化的程度。假设:系统A在10~20个用户的情况下,请求响应时间都是0.5秒;系统B在10个用户的情况下,请求响应时间是0.2秒,在20个用户的情况下,请求响应时间上升到2秒。此时,系统A的负载敏感度比系统B低。我们还可以使用术语 衰减 (degradation),称系统B衰减得比系统A快。
效率 是性能除以资源。如果一个双CPU系统的性能是30tps,另一个系统有4个同样的CPU,性能是40tps,则前者效率高于后者。
系统的 容量 是指最大有效负载或吞吐率的指标。它可以是一个绝对最大值或性能衰减至低于一个可接受的阈值之前的临界点。
可伸缩性 度量的是向系统中增加资源(通常是硬件)对系统性能的影响。一个可伸缩的系统允许在增加了硬件后,能够有性能上的合理提高。例如,为了使吞吐率提高一倍,要增加多少服务器等。 垂直可伸缩性 或称 垂直延展 ,通常指提高单台服务器的性能,例如增加内存。 水平可伸缩性 或称 水平延展 ,通常指增加服务器数目。
问题是,设计决策对所有性能指标的作用并不相同。比如,在某台服务器上运行着两个软件系统:Swordf ish的容量是20tps,而Camel的容量是40tps。哪一个的性能更高?哪一个的可伸缩性好?仅凭这些数据,我们无法回答关于可伸缩性的问题,我们只能说Camel系统在单服务器上的效率更高。假设又增加了一台服务器后,我们发现:Swordf ish的容量是35tps,Camel的容量是50tps。尽管Camel的容量仍然大于Swordf ish,但是后者看起来可能更好扩展。假设我们继续增加服务器数目后发现:Swordf ish每增加一台服务器提高15tps,Camel每增加一台服务器提高10tps。在获得了这些数据后,我们才可以说,Swordf ish的水平可伸缩性比Camel好,尽管Camel在5个服务器以下会有更好的效率。
当构建企业系统时,关注硬件的可伸缩性往往比关注容量或效率更重要。如果需要,可伸缩性可以给予你获得更好性能的选择,可伸缩性也可以更容易实现。有时,设计人员费了九牛二虎之力才提高了少许容量,然而实际上购买更多硬件可能更便宜。如果Camel的成本高于Swordf ish,并且这一成本相当于几台服务器,那么Swordf ish最终会更便宜,即使你只需要40tps。现在人们经常抱怨软件对硬件的依赖性越来越大,有时为了运行某些软件就不得不对硬件进行升级,就像我一样,为了用最新版本的Word,就必须不断地升级笔记本电脑。但是总的来说,更新的硬件通常比在不那么强大的系统上运行软件要便宜。同样,增加更多的服务器也比增加更多的程序员来得便宜——只要你的系统有足够的可伸缩性。