8

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

(注意:刚刚了解了Big O Notation,我希望在这里收集更多编程原则。我试图通过在下面回答我自己的问题来解释 Big O Notation,但我希望社区能够改进这个问题和答案。)

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

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

4

7 回答 7

11

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

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

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

于 2008-09-03T09:41:49.140 回答
6

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

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

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

浏览器 -> Web 服务器 -> 应用服务器 -> DB -> 应用服务器 -> XSLT -> Web 服务器 -> 浏览器 JS 引擎执行和渲染

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

假设一切都是同步的(没有需要等待的基于队列的事情),性能和/或可伸缩性问题的机会很多。但是由于您的帖子是关于可伸缩性的,所以让我们忽略浏览器,除非任何远程 XHR 调用会从 Web 服务器调用另一个请求/响应。

因此,鉴于这个问题域,您可以做出哪些决定来帮助实现可扩展性?

  1. 连接处理。这也与会话管理和身份验证有关。在不影响安全性的情况下,它必须尽可能干净和轻便。指标是每单位时间的最大连接数。

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

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

  4. 或映射器效率低下。对象设计和关系设计之间存在阻抗不匹配。RDBMS 中的多对多结构与对象层次结构(person.address 与 location.resident)直接冲突。数据结构越复杂,OR 映射器的效率就越低。在某些时候,您可能不得不在一次性情况下减少诱饵并采取更多......呃......原始数据访问方法(存储过程 + 数据访问层),以便从特定的丑陋的模块。了解所涉及的成本并做出有意识的决定。

  5. XSL 转换。XML 是一种出色的、标准化的数据传输机制,但它可以是一只巨大的性能狗!根据您随身携带的数据量、您选择的解析器以及您的结构的复杂程度,您可以使用 XSLT 轻松地将自己描绘成一个非常黑暗的角落。是的,从学术上讲,这是一种非常干净的表示层方式,但在现实世界中,如果您不特别注意这一点,可能会出现灾难性的性能问题。我已经看到一个系统仅在 XSLT 中就消耗了超过 30% 的事务时间。如果您想在不购买额外盒子的情况下将用户群扩大 4 倍,那就不好看了。

  6. 你能从可扩展性的困境中购买出路吗?绝对地。我已经看到它发生的次数比我想承认的要多。摩尔定律(正如您已经提到的)今天仍然有效。准备一些额外的现金以防万一。

  7. 缓存是减少引擎压力的好工具(提高速度和吞吐量是一个方便的副作用)。尽管就内存占用和在缓存过时时使缓存无效的复杂性而言,这是有代价的。我的决定是从完全干净的开始,然后慢慢地只在你认为对你有用的地方添加缓存。很多时候,复杂性被低估了,而最初作为解决性能问题的一种方式,结果却导致了功能问题。另外,回到数据使用评论。如果您每分钟都在创建数千兆字节的对象,那么是否缓存并不重要。你会很快耗尽你的内存占用,垃圾收集会毁了你的一天。所以我想外卖是要确保你明白到底是什么'

很抱歉冗长。刚滚,忘了抬头。希望其中的一些内容能触及您的调查精神,而不是太初级的对话。

于 2008-09-16T21:21:31.017 回答
4

好吧,有一个名为High Scalibility的博客,其中包含有关此主题的大量信息。一些有用的东西。

于 2008-09-03T09:38:25.717 回答
3

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

确定缩放对您的项目的实际意义。是无限数量的用户,它是否能够在网站上处理斜线,它是开发周期吗?

使用它来集中您的开发工作

于 2008-09-03T09:38:39.757 回答
2

Jeff 和 Joel 在Stack Overflow 播客 #19中讨论了扩展。

于 2008-09-03T09:33:13.360 回答
1

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

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

解决此问题的一种方法是:

    每辆车{
       确定我的位置;  
       每辆车{  
         将我的位置添加到这辆车的地图上;  
       }
    }

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

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

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

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

    每辆车{  
      将我的职位添加到列表中;  
    }  
    每辆车{    
      给我一份主清单的更新副本;  
    }

使用这种策略,步数是 N 的倍数,而不是 N 2的倍数。所以 100 辆汽车需要 1 辆汽车 100 倍的工作量,而不是 10,000 倍的工作量

这个概念有时用“大 O 表示法”表示——所需的步数是“N 的大 O”或“N 2的大 O ”。

请注意,此概念仅与可扩展性有关,而不是优化每辆车的步数。在这里,我们不在乎每辆车走 5 步还是 50 步——主要是 N 辆车走 (X * N) 步,而不是 (X * N 2 )。

于 2008-09-03T09:56:07.500 回答
1

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

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

如果您认为您的应用程序很可能需要扩展,那么在开发的相对早期研究 memcached 或 map reduce 等系统可能是明智之举。

于 2008-09-03T09:57:30.320 回答