可伸缩性,美妙的可伸缩性
本文最初发表在 MSDN Online Voices 的 Diving Into Data Access 专栏(http://msdn.microsoft.com/voices/data.asp [英文])。
很多年以前,当我还是一个害羞的初级程序员时,每次了解项目管理中枢信息的机会对我来说都像是幸运之神对我的眷顾。不管是偶尔被正式邀请参加高层会议,还是鬼鬼祟祟地躲在桌子底下偷听,多年以来,我不断地听到“可伸缩性”这个词在耳边重复,像一个令人厌烦的老调。“我们如何提高可伸缩性?”,“您提出的方法有足够的伸缩性吗?”,“在中间层我们需要更多的可伸缩性。”
可伸缩性,美妙的可伸缩性。但是,什么是可伸缩性呢?
无论它是什么,我想作为我的职责,我应该重新引起大家对可伸缩性问题的关注。事实证明,您永远不知道项目管理这一领域正在进行着什么样的变化。
我还记得曾在许多学术书籍中频繁地看到可伸缩性这个词,这些书宣称涵盖了分布式系统的原则和技术。“在一个好的设计中,可伸缩性永远是决定性因素。”
可伸缩性,美妙的可伸缩性。但是,究竟什么是可伸缩性呢?
最近,我发现“可伸缩”一词已经成为人们最喜欢的一种“工具”,用来衡量用于分布式环境的所有软件技术在功能方面的质量。因此给我一种感觉,我应该在自己的可伸缩性方面进行更大的投资,以期我的事业更加兴旺发达。问题也就出来了。归根到底,可伸缩性到底意味着什么?在如今的数据库服务器和硬件环境中,它又意味着什么?
可伸缩性方面的成绩
抽象地来讲,我认为可以通过两种基本方法来利用可伸缩性的重要属性对系统进行修饰。一种是在设计阶段从理论上对系统进行优化。将其从所有软件工具和基础元素中独立出来进行考虑,然后再将它应用于实践。从实际意义上来讲,这意味着:在发掘任务的自然并行度时,尽可能将瓶颈和关键资源的影响减到最小。
而另一种方法则恰恰相反。您只能控制某些硬件和软件的内置功能,您自己的解决方案和创造性发挥的余地比较小。基本上,可伸缩性和互操作性方面的责任都交给选定用于处理此任务的工具来完成。
顺便说一句,在人们着重关注性能和可靠性而似乎忽略了可伸缩性的前 Internet 时代,第一种方法已经使用若干年了。(在没有实现大范围 Internet 连接的情况下,这的确是一个相对的问题。)
几年前,由于功能丰富并且负担得起的集成解决方案比较缺乏,因此人们主要考虑的是设计问题,把注意力放在数据库结构和各种操作的运算所消耗的资源上。
今天,较为便宜并且功能强大的硬件的出现使优化和设计的有效性在项目的整体管理中已经退居次要地位,这种可伸缩性独立于硬件的缺点已经被克服。
计算复杂程度的重要规则表明,只有最快的算法才能最充分地利用更快的硬件。考虑可伸缩性问题时请注意这一点。
增长因素
从一般意义上来说,可伸缩性是指客户端数量增加时,系统维持(如果没有提高)其平均性能的能力。因此,可伸缩性说起来是一个简洁明了的概念。
但是,可伸缩性也可以说是抽象的。因为它不是一种可以通过编程手段来打开和关闭,或以某种方式直接控制的系统属性。相反,它是一种系统特性,是所有其他特性、总体设计和实现方案以及所选择的交互模型的组合。
分布式系统中的可伸缩性的内在级别不易于通过监视和分析工具进行检测。另一方面,许多实现方面(重要资源是否丰富、设计的瓶颈问题、冗长但必要的任务以及操作过分的序列化)的因素限制了可伸缩性。
但是如果不在实际的产品环境下对系统进行强度测试,就不能够判断某个特定系统是否具有足够的可伸缩性。
可伸缩性在某种程度上与性能有关,如果系统构造良好并且使用了合理而完备的计划,这将不是问题。
看到像可伸缩性这样一个不可见的系统特性突然之间成为使系统性能下降的主要原因,这一定会让人感到惊讶。不管系统未来如何演变,只要在设计组件时适当注意操作的运算所消耗的资源,您就可以立于不败之地。
可伸缩性一直是一个影响系统增长的因素。概括地说,需要增长的系统是指其当前性能不能满足预期用户数量需要的系统。如何从结构上改善此类系统的性能?简单地说,只需要安装功能更强大的硬件或更好的硬件和软件组合。
如今,这两个选项已经被更改为更具迷惑力、更具市场战略的表达方式。以硬件为中心的方式称为“扩大”。硬件和软件相结合称为“扩展”,这种结合更为巧妙。
要控制系统的增长,请确保进行扩大或者扩展。但是,只有维护系统的性能才能实现扩大或扩展。
可伸缩性,美妙的可伸缩性。这是最合适的定义吗?
扩大
扩大系统基本上就是采用更新过的、功能更强大的硬件系统,如同加固雨伞的伞骨和伞柄。一旦新系统就绪,便可以备份表和应用程序并正式开始运行。这样,对现有代码和系统组织结构的影响将减至最低限度。
然而,不要以为进行扩大的路上铺满玫瑰。扩大也有其不足的一面,其中有几点需要多加注意。首先(也是最重要的),扩大的系统存在一个可能会导致失败的缺陷,这样的系统最终会面临某种硬件方面的限制。扩大通过使用功能更强的计算机,从而增加服务器的处理能力。单个硬件处理能力的增长有一个物理上的上限,虽然目前人们还可能无法预见,但终有一天会达到此值。
第二,接近此上限需要相当可观的花费,这种花费既包括时间(超过一定的限度后,也许需要花若干年才能在技术上成倍地提高性能)、费用,还包括(最后但并非最不重要)电源消耗和办公室空间。
这就是说,由于对现存结构的影响有限,扩大是一种比较合理的首选方案。
扩展
扩展是指增加系统整体处理能力,这与增加作为服务器的单个部件的能力正好相反。扩展的系统本质上是模块化的,并由一个计算机群集组成。扩展这样的系统意味着为系统添加一台或更多附加计算机。
在经过扩展、高度分区的环境中,您采用的处理能力应当更加抽象、更不依赖于硬件。总的处理能力是每台计算机的实际速度之和,而每台计算机又由整个节点范围内数据和应用程序的分区来调节。
看起来扩展对系统的增长似乎没有限制。开始从这点来看无疑是有利的。但是,扩展却涉及到大量的重新设计和重新实现方面的工作。根据单服务器理论而构造的系统必须经过重新考虑并重新组织,才能满足扩展要求。
您必须确定如何对跨多个 DBMS 服务器的数据进行分区。应用程序到达数据的路径必须通过适当的执行计划进行认真优化。在其生命周期中,对用户活动性应该经常进行良好的分析,这对调整系统是至关重要的。
做到这些以后,您便拥有了一个虚拟的无限系统,您可以在中间层和数据层添加处理资源,以满足不断增长的用户和工作量的需要。
请注意,对于可伸缩的系统,关键问题不在于期望的用户数量可以达到多少。真正要考虑的是期望此数量以多快的速度增长。数量的相对增长比绝对数量要重要得多。
与为相对稳定的一亿个用户设计一个系统相比,构建一个开始有一百个用户,但随着时间推移用户数量将成倍增长的系统要复杂得多。因此,对行业影响重大的电子商务 Web 应用程序对可伸缩性的需求特别强,因为这些系统将要面临突发地大幅增长的用户数量。
SQL Server 2000 如何进行扩展
可以肯定,扩大相对来说比较容易实现,并且对于数据和用户数量较少的情况来说,成本也比较低。但从软件方面来说,扩展技术却更有趣、也更具有挑战性。没有在后端工作的现有软件的帮助,您不可能合理地进行扩展。
您在 COM+ 和 Windows® 2000 中发现的群集模型就是一个扩展模型。业务层中的所有服务器都具有相同的 COM+ 组件副本。在后台运行的 Windows 2000 负载平衡服务程序将负责根据各个组件的工作量调度对组件的新请求。
从应用程序的观点来看,您看到的是一个单独实体,即 COM+ 组件集。您根据这些组件进行编码,而不必考虑运行这些组件的不同服务器的数量,并且可以忽略基本负载平衡器的作用。此处的扩展就像添加已安装适当组件集的、经过全新配置的 Windows 2000 服务器一样简单。
在此群集模型中,两个截然不同的实体协同工作:应用程序组件和系统负载平衡器。这样一个模型要应用到数据层并不容易。事实上,您只有一个软件实体——DBMS。
SQL Server 2000 支持另外一种群集模型,称为“联合服务器”。此网络使应用程序可以看见一组运行 SQL Server 的服务器。所有这些 SQL Server 公共实例都是通过不同的表甚至不同的配置设置来独立管理的。
DBMS 主要的工作量是数据。应用程序主要负责均衡地将数据分发到多个服务器。SQL Server 2000 本身提供内置功能,支持分布在多个服务器上的数据的可更新视图。
您可以通过网络上的多个 SQL Server 实例来确定扩展表。无论何时,您都可以根据需要将这些数据捆绑在一起。您可以通过“分区视图”来完成此步骤,这种特殊的视图可以享受 SQL Server 2000 运行时的特殊支持。
分区视图
分区视图应用于“联合表”,即跨两个或多个服务器而生成的关键表。联合表通过称为“水平分区”的处理程序来创建,该程序将给定的表分为较小表的联合体。从应用程序的角度来看,联合表就像一个单独的视图。
为了平衡工作量,成员表被置于各个不同的机器中。它们的格式与原始表相同,但每个表只包含一部分行。成员表可以使用任意名称,但是为了增加位置透明度,建议使用原始表名称为成员表命名。
构成表通常设计为包含相同大小的数据部分。这一特性是通过一个唯一的、不可更新的主键完成的。要确保完整性和一致性,您还必须确保没有重复的记录。要做到这一点,建议您对每个成员表执行 CHECK 限制。分区列不能为空或为计算列。
分区视图是包含分布式 SELECT 语句的普通视图,这些语句适用于结构相同的表,并通过 UNION ALL 子句将数据放在一起。
CREATE VIEW MyView AS
SELECT * FROM Server1.Database.Owner.MyTable
UNION ALL
SELECT * FROM Server2.Database.Owner.MyTable
UNION ALL
SELECT * FROM Server3.Database.Owner.MyTable
UNION ALL
SELECT * FROM Server4.Database.Owner.MyTable
此分区视图必须在相关的服务器上创建,并且每个服务器都必须以链接服务器的形式对其他服务器可见。最好将它们的 lazy schema validation 选项设置为 true。该属性可以确定是否检查远程链接的架构。将其设置为 true,Server 将跳过检查。虽然这种检查会使性能更好,但在此特殊情况下,设置为 lazy 不会有丝毫的副作用。
分区视图最初是随 SQL Server 7.0 引入的。然而,随着 SQL Server 2000 的推出和一些重要改进的出台,它们成为扩展系统的重要工具。
在 SQL Server 2000 中,可以更新分区视图并且可以通过查询优化程序进行特殊优化,该优化程序旨在最大程度地减少对跨服务器处理的需要。联合表的主要优势在于平衡服务器之间的工作量。只要所有的服务器都能在本地完成指派的任务,这无疑是一个优势。
在以下情况下,分区视图将自动更新:
它是由使用 UNION ALL 子句的单个 SELECT 语句合并的结果组成的。
每个 SELECT 语句在一个单独的表上工作(即不允许使用 JOIN)。
该表是本地表或链接表。
该表包含时间标记列。
链接表必须使用以下可能条件进行引用:完全合格的名称(服务器、数据库、所有者、表名)、OPENROWSET 或 OPENDATASOURCE 函数。
针对可更新分区视图运行的 INSERT、UPDATE 和 DELETE 语句必须满足一系列限制条件才能生效。例如,如果表包含一个标识列,则不能插入新行。您必须指定所有列,包括带有 DEFAULT 限制的列。如果该视图是自联接或与任何其他成员表联接,则不允许进行更新或删除。
扩展的实践
SQL Server 2000 发布的群集模型并非适用于所有人。它是为高端 OLTP 企业系统和某些主动型 Web 应用程序而专门设计的。
为了提高效率,它要求数据分区,而且分区必须遵循逻辑架构。所有相关数据必须位于同一服务器,并且必须能够进行逻辑拆分。
对数据的深入理解是绝对必要的。另外,随着时间的推移,数据的形状不应有太多变化。如果您知道它将发生变化,则应该预先了解它将来的形状,并在规划分区时考虑到这一点。
在相同的节点上存放相关数据是可行的,否则在网络滞后时间中您将很快丢失您使用精心设计的负载平衡策略所得到的内容。
完成数据分区后,即使您非常成功并且聪明过人,您也只是完成了一半。实际上,将数据真正移动到选择的群集、安排备份以及监视解决方案都还有待于您来解决。
与扩大相比,扩展技术确实难以实现并且是一种更麻烦的方式。设计问题集中表现为一些现实的障碍,例如缺乏特定工具将扩展群集作为单个实体进行管理。其中一些工具预计将在下一版的 SQL Server 中提供,代码名称为 Yukon。
扩展可伸缩性看起来前景很光明也很诱人,但是,在单个服务器上进行扩大在多数情况下仍然是最保险的方式。
扩大还是扩展?
事实上,第三代 Web 服务对硬件方面有严格的要求。您原则上需要解决的问题是:选择扩大还是扩展?
随着单个硬件系统功能的增强,对扩大的需求也将增加。而扩展会使您的系统成为包含多个系统且不断增长的集合、这些系统相互连接,但规模较小。
扩大的系统更有可能出现故障,内在可靠性较差,并且超过某个阈值后不能进行升级。从电源消耗、空间和价格方面来说,它也比较昂贵。但另一方面,对系统进行扩大就像备份和恢复一样简单而无缝。
尽管处理单个机器总是比处理成打或成百个机器容易,但扩展系统具有更强的内在可靠性和可扩展性,并且整体成本更低。
然而,这两种方式的利与弊都是相对而言的,并且完全是针对特定项目而言的。
扩大还是扩展取决于系统的内在特点。如果通过单个 SQL Server(可能在单独的多处理器上运行)可以进行您所需要数据的访问,则扩展是首选方案。附带说明,这是 Web 领域所使用的可伸缩性模型。
但是,Web 领域扩展模型只能从一个方面来看待水平可伸缩性。如果您需要处理较大数量的并发数据(例如,OLTP 系统),您可能需要循序渐进地处理协同工作的多个服务器。在此情况下,必须适当地重新组织数据。如此巨大的工作量不可能在短时间内完成。在着手开始这样的项目之前,请确保您的系统已准备就绪,能够胜任如此海量的工作。也就是说,请确保可以将其当作 OLTP 系统使用。
按一般规则来说,尽管扩展看起来更有发展前途,但还是应始终首先考虑扩大,只有在理由充分时才放弃扩大。
可伸缩性是发展的趋势
不管扩大和扩展技术的理论怎样,有几点需要注意:
可伸缩性与计算的复杂性有关,并且间接地与性能有关。
只有在协调好任务的最佳计算复杂性和实现力量后,才能考虑是通过硬件进行扩大还是使用特殊数据库服务进行扩展。
然而,据我所知,与改进算法、优化查询执行计划和找到并消除瓶颈相比,采用速度更快的硬件总是要来得快些。并且它还可以帮助您按时完成任务。
对话:围绕 ADO 的争论
我的老板曾经问我:“ADO 和 ADO.NET 之间有什么区别?”我当时告诉他 ADO.NET 是另外一个数据访问层,我们即使在用过之后,也不会完全明白其利与弊。后来我发现 ADO 代码在 .NET 中也可以正常工作,但您有关 ADO 的摇滚乐似的文章使我的信心大打折扣。
我承认,乍看上去,很容易把 ADO.NET 想成“另外一个数据访问层”。但再仔细看看,就不是那么回事了。对于在 .NET 下运行的数据处理应用程序,ADO.NET 一直是并且仍然会是唯一建议使用的方法。
最近几年,我发现人们争先恐后地使用 ADO,并放弃经过优化和调整的 RDO 代码。除了异常结果之外,他们获得了其他所有想要的功能。然而,只有可怜的 ADO 倍受责难。
同样,我敢说可怜的 ADO.NET 将成为糟糕的迁移项目的替罪羊。虽说没有完美的代码,但系统代码几乎总是比你我的代码好。就是说,RDO 到 ADO 的升级应该是有问题的。为什么仅仅为了从 ODBC 升级到 OLE DB 和更强大的对象模型,就要去更改那些运行良好的代码呢?
使用 ADO.NET,事情就完全不同了。如果您选择 .NET 作为服务器平台,ADO.NET 便是您当然的选择。ADO.NET 是您能用于处理数据的唯一的类集合。
从技术上讲,您可以坚持使用 ADO,但您最终将在 .NET 到 COM 的转换上付出高昂的代价。ADO.NET 的关键在于开始真正的编码之前,您可以学习它并在某种受到控制环境中使用它。在升级至 ADO 之前,有多少人这样做过?
对于今天这个由 Web 驱动的世界来说,相对 ADO 而言,ADO.NET 是更适用的模型。同时,ADO.NET 对象模型已经尽可能多地兼容了 ADO 的概念,并在适当的、足以说明问题的情况下使用了这些概念。要转至 ADO.NET,从现在就开始,您会发现学习过程原来如此简单快捷,简直令人难以置信。开发人员不必学习太多的新概念,就可以使用 ADO.NET。
但是人们必须重新设置他们的 ADO 式的思维框架,并且要开始按照另一个模型进行思考。ADO.NET 绝不是“另外一个数据访问层”。您不必在 ADO 和 ADO.NET 之间进行选择。主要的选择就是决定您是否要使用 .NET 平台。