一般来说,什么样的设计决策有助于应用程序很好地扩展?

(笔记:刚刚了解到 大 O 表示法, ,我希望在这里收集更多的编程原理。我试图通过回答下面我自己的问题来解释大 O 表示法,但我希望社区改进这个问题和答案。)

到目前为止的回应
1) 定义缩放比例。您是否需要针对虚拟环境中的大量用户、流量、对象进行扩展?
2)看看你的算法。他们所做的工作量是否与实际工作量成线性比例 - 即要循环的项目数量、用户数量等?
3)看看你的硬件。您的应用程序是否经过设计,以便在一台计算机无法跟上时可以在多台计算机上运行它?

次要想法
1)不要太快优化太多——先测试。也许瓶颈会发生在意想不到的地方。
2)也许扩展的需求不会超过摩尔定律,也许升级硬件会比重构更便宜。

有帮助吗?

解决方案

我唯一想说的是编写您的应用程序,以便从一开始就可以将其部署在集群上。任何高于这个值的优化都是过早的。你的第一份工作应该是获得足够的用户来解决扩展问题。

首先构建尽可能简单的代码,然后分析系统,仅在存在明显的性能问题时进行优化。

通常,分析代码得出的数据是违反直觉的;瓶颈往往存在于您认为不会很慢的模块中。在优化方面,数据为王。如果你优化你认为会很慢的部分,你通常会优化错误的东西。

其他提示

好的,您已经抓住了使用“大 O 表示法”的关键点。如果你不注意的话,这一维度肯定会从背后咬你一口。还有其他一些维度在起作用,有些人无法通过“大O”眼镜看到这些维度(但如果你仔细观察,它们确实是这样)。

该维度的一个简单示例是数据库连接。在构造左内连接等方面有一些“最佳实践”,这将有助于使 sql 更有效地执行。如果您分解关系演算,甚至查看解释计划 (Oracle),您可以轻松查看哪些索引以何种顺序使用,以及是否发生任何表扫描或嵌套操作。

分析的概念也很关键。您必须在架构的所有移动部分中以正确的粒度进行彻底的检测,以便识别和修复任何低效率的问题。举例来说,您正在构建一个 3 层、多线程、基于 MVC2 Web 的应用程序,该应用程序充分使用 AJAX 和客户端处理以及应用程序和数据库之间的 OR 映射器。简单的线性单个请求/响应流程如下所示:

browser -> web server -> app server -> DB -> app server -> XSLT -> web server -> browser JS engine execution & rendering

您应该有一些方法来测量每个不同区域的性能(响应时间、以“每单位时间的内容”测量的吞吐量等),而不仅仅是在机器和操作系统级别(CPU、内存、磁盘 I/O、等),但特定于每一层的服务。因此,在 Web 服务器上,您需要了解您正在使用的 Web 服务器的所有计数器。在应用程序层,您需要了解您正在使用的任何虚拟机(jvm、clr 等等)。大多数 OR 映射器都在虚拟机内部显现,因此请确保您关注所有细节(如果它们在该层对您可见)。在数据库内部,您需要知道 一切 正在执行的命令以及适合您的数据库风格的所有特定调整参数。如果您有大笔资金,BMC Patrol 是一个相当不错的选择(具有适当的知识模块 (KM))。在便宜的情况下,您当然可以自己动手,但您的里程会根据您的专业知识深度而有所不同。

假设一切都是同步的(没有需要等待的基于队列的事情发生),那么有大量的机会出现性能和/或可扩展性问题。但由于您的帖子是关于可扩展性的,因此我们忽略浏览器,除了任何将调用来自 Web 服务器的另一个请求/响应的远程 XHR 调用。

那么,考虑到这个问题领域,您可以做出哪些决策来帮助实现可扩展性?

  1. 连接处理。这也与会话管理和身份验证相关。它必须尽可能干净和轻量级,同时又不影响安全性。该指标是每单位时间的最大连接数。

  2. 每层的会话故障转移。有必要还是没必要?我们假设每一层都是在某种负载平衡机制下水平排列的一组盒子。负载平衡通常非常轻量级,但会话故障转移的某些实现可能比预期的更重。此外,您是否使用粘性会话运行可能会影响您在架构中更深入的选择。您还必须决定是否将 Web 服务器绑定到特定的应用程序服务器。在 .NET 远程世界中,将它们连接在一起可能更容易。如果您使用 Microsoft 堆栈,那么执行 2 层(跳过远程处理)可能更具可扩展性,但您必须做出重大的安全权衡。在java方面,我总是看到至少有3层。没有理由这样做。

  3. 对象层次结构。在应用程序内部,您需要尽可能最干净、最轻量的对象结构。仅在需要时携带所需的数据。恶意删除任何不必要或多余的数据获取。

  4. 或者映射器效率低下。对象设计和关系设计之间存在阻抗不匹配。RDBMS 中的多对多构造与对象层次结构(person.address 与 person.address)直接冲突。位置.居民)。数据结构越复杂,OR 映射器的效率就越低。在某些时候,您可能必须在一次性情况下放弃诱饵,并采取一种更……呃……原始的数据访问方法(存储过程+数据访问层),以便从特定的环境中获得更多的性能或可扩展性。丑陋的模块。了解所涉及的成本并做出明智的决定。

  5. XSL 转换。XML 是一种美妙的、规范化的数据传输机制,但它是一条巨大的性能狗吗!根据您随身携带的数据量、您选择的解析器以及您的结构的复杂程度,您可能很容易将自己陷入 XSLT 的黑暗角落。是的,从学术上讲,这是一种非常干净的表示层方法,但在现实世界中,如果您不特别注意这一点,可能会出现灾难性的性能问题。我发现系统仅在 XSLT 中就消耗了超过 30% 的事务时间。如果您想在不购买额外盒子的情况下将用户群增加 4 倍,那就不太好了。

  6. 您能通过购买方式摆脱可扩展性困境吗?绝对地。我亲眼目睹这种事发生的次数比我愿意承认的还要多。摩尔定律(正如您已经提到的)今天仍然有效。手边准备一些额外的现金以防万一。

  7. 缓存是减少引擎压力的好工具(提高速度和吞吐量是一个方便的副作用)。但它的代价是内存占用和缓存失效时的复杂性。我的决定是从完全清理开始,然后仅在您认为对您有用的地方慢慢添加缓存。很多时候,复杂性被低估了,最初作为解决性能问题的方法最终导致了功能问题。另外,回到数据使用评论。如果您每分钟创建千兆字节的对象,那么是否缓存并不重要。你很快就会耗尽你的内存占用,垃圾收集会毁了你的一天。所以我想要点是确保您准确了解虚拟机内部发生的情况(对象创建、销毁、GC 等),以便您可以做出最佳决策。

抱歉啰嗦了。刚刚打滚,忘记抬头了。希望其中的一些内容能触及您的探究精神,而不是太初级的对话。

嗯,有一个博客叫做 高扩展性 其中包含有关该主题的大量信息。一些有用的东西。

通常,最有效的方法是通过深思熟虑的设计,其中缩放是其中的一部分。

确定扩展对您的项目实际意味着什么。是否有无限数量的用户,是否能够处理网站上的斜线,是否是开发周期?

用它来集中您的开发工作

杰夫和乔尔讨论了扩展 堆栈溢出播客#19.

FWIW,大多数系统将通过忽略这一点来最有效地扩展,直到它成为一个问题 - 摩尔定律仍然有效,除非您的流量增长速度快于摩尔定律,否则通常购买更大的盒子会更便宜(每个盒子 2 或 3K 美元) pop)而不是付钱给开发商。

也就是说,最重要的关注点是数据层;这是应用程序中最难扩展的部分,因为它通常需要具有权威性,并且集群商业数据库非常昂贵 - 开源版本通常很难正确使用。

如果您认为您的应用程序很可能需要扩展,那么在开发过程中相对较早地研究诸如 memcached 或 MapReduce 之类的系统可能是明智之举。

一个好主意是确定每项附加任务会产生多少工作量。这可能取决于算法的结构。

例如,假设您在一个城市中有一些虚拟汽车。在任何时候,您都希望每辆车都有一张显示所有汽车所在位置的地图。

解决这个问题的一种方法是:

    for each car {
       determine my position;  
       for each car {  
         add my position to this car's map;  
       }
    }

这看起来很简单:查看第一辆车的位置,将其添加到其他所有车辆的地图中。然后查看第二辆车的位置,将其添加到其他所有车辆的地图中。ETC。

但存在可扩展性问题。当有 2 辆车时,该策略需要 4 个“添加我的位置”步骤;当有3辆车时,需要9步。 对于每个“位置更新”,您必须循环浏览整个汽车列表 - 并且每辆车都需要更新其位置。

忽略每辆车必须做多少其他事情(例如,可能需要固定数量的步骤来计算单个汽车的位置), 对于 N 辆车,需要 N2 “访问汽车”来运行该算法. 。当您有 5 辆车和 25 个台阶时,这没有问题。但当你添加汽车时,你会发现系统陷入困境。100辆车需要走10,000步,101辆车需要走10,201步!

更好的方法是撤消 for 循环的嵌套。

    for each car {  
      add my position to a list;  
    }  
    for each car {    
      give me an updated copy of the master list;  
    }

采用这种策略,步数是N的倍数,而不是N的倍数2. 因此,100 辆汽车将花费 1 辆汽车 100 倍的工作量,而不是 10,000 倍的工作量.

这个概念有时用“big O notation”来表达——所需的步骤数是“big O of N”或“big O of N”2."

请注意,这个概念只涉及可扩展性,而不是优化每辆车的步数。这里我们不关心每辆车需要 5 步还是 50 步 - 最主要的是 N 辆车需要 (X * N) 步,而不是 (X * N)2).

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top