可伸缩性 Scalability

• 本文约 2100 字,阅读大致需要 5 分钟 | Others | #performance | #scalability | #分布式系统

关于Scalability这个词的中文译法,目前还没有一个非常确切的定论。一些文献中将其翻译为"可扩充性",而"扩充"指的主要是Scale up;而在一些实际的用法中,Scalability还包括Scale down,因此,“可伸缩性"也许是比较贴近原文意思的说法。

互联网应用的可扩充性体现在两个方面:其一,是能够对系统容量进行扩充,也就是说,这个系统各个组件能够提供的服务的量,能够在需要的时候予以扩充(特别是通过添加新的服务器等等)。其二,是这种扩充是有效率的扩充,即,增加硬件投入时,其投入与所产生的效果是接近甚至达到成比例增加的。通常说来,“可扩充"同时暗含的需求是用户的使用习惯尽可能保持不变。如果我们关注某一具体的计算节点,可扩充性还应体现于提高计算节点性能,例如增加其CPU数量或内存容量时,能够相应地改善系统的容量或响应时间,等等。

而另一方面,“可缩减性"主要指的则是说一套系统能够运行在尽可能少的软硬件环境之中。对于大型互联网公司而言,这一点可能并不重要,而对初创公司来说这一点则非常重要。

在设计互联网应用的时候,充分地考虑系统的可伸缩性,能够极大地减少日后的维护开销,并帮助决策者对于投资所能获得的回报进行更加精准的估计;另一方面,高可伸缩性的系统往往会具有更好的容灾能力,从而提供更好的用户体验。

与解决很多其他问题类似,改善可伸缩性最常用的方法就是分治法(Divide and Conquer)。分治属于大道理一类,在实践中,我们比较常用的分治策略包括:

个人的一些经验和用到的技巧:

  1. 减少不必要的节点间通讯。让计算能够在尽可能多的地方进行这个目标,与尽可能让计算在靠近数据存储的地方进行并不矛盾。说的更露骨一些,不能变成现金的流量和服务器端计算都要尽可能减少。
  2. 让一个会话中不太重要,但共享并修改同一份数据的部分,尽可能避免其在不同的计算节点之间漂移。这与减少不必要的节点间通讯的目标一致,同时,还能够有效地降低程序设计的复杂性。
  3. 减少环节数。需要n个环节完成的一个事物,就会有n个可能发生问题的点。每个环节能够承担其任务的节点越多,在那个环节发生问题的可能性就越小。
  4. 可能的时候,尽量将计算推迟到拥有尽可能多相关数据,或靠近这些数据的节点进行。例如,如果一个用户界面中需要对用户可见的200行数据按照用户需要进行排序或筛选,就可以考虑把排序过程放在客户机一端,而不一定是在服务器上去做,因为服务器做这件事并不一定更快,而另一方面这一设计也会减少服务器在提供响应时话费的时间,而无需增加额外的网络流量。
  5. 设计好容灾方案,特别是故障转移方案。假设每个节点发生问题的概率一样,系统中的节点越多,用户访问全部节点时,遇到一个有问题的节点的机会就越多。好的容灾方案则能够让用户最终真的看到的问题数量与节点数接近成反比,因此,合理的故障转移方案能够让用户尽可能不看到节点发生故障的情形。
  6. 避免借助"外力"去检测问题。例如,如果在一台前端服务器上的查询客户端的查询过程本身也是进行健康检查/反馈的过程,除了能够节省少量用于健康检查的带宽开销之外,还能够提供更及时有效的反馈。
  7. 不必过分强调单个节点的性能,除非那个节点没有办法扩充为较大的、可并发的系统(这种情况非常罕见)。为了强调单个节点性能而牺牲可并行或者可并发性能是得不偿失的。
  8. 在设计网络时,尽量避免将不相关的网络所在的物理设备连接到一起,必要时才通过VLAN来进行隔离。

以上是一些个人经验的总结,纯属抛砖引玉。