文章归档

可伸缩性最佳实践:来自eBay的经验

作者 Randy Shoup 译者 郭晓刚

在eBay,可伸缩性是我们每天奋力抵抗的一大架构压力。我们所做的每一项架构及设计决策,身前身后都能看到它的踪影。当我们面对的是全世界数以亿计的用户,每天的页面浏览量超过10亿,系统中的数据量要用皮字节(1015或250)来计算——可伸缩性是生死交关的问题。
在一个可伸缩的架构中,资源的消耗应该随负载线性(或更佳)上升,负载可由用户流量、数据量等测量。如果说性能衡量的是每一工作单元所需的资源消 耗,可伸缩性则是衡量当工作单元的数量或尺寸增加时,资源消耗的变化情况。换句话说,可伸缩性是整个价格-性能曲线的形状,而不是曲线上某一点的取值。

可伸缩性有很多侧面——事务的方面、运营的方面、还有开发的方面。我们在改善一个Web系统的事务吞吐量的过程中学到了很多经验,本文总结了其中若 干关键的最佳实践。可能很多最佳实践你会觉得似曾相识,也可能有素未谋面的。这些都是开发和运营eBay网站的众人的集体经验结晶。

最佳实践 #1:按功能分割

相关的功能部分应该合在一起,不相关的功能部分应该分割开来——不管你把它叫做SOA、功能分解还是工程秘诀。而且,不相关的功能之间耦合程度越松散,就越能灵活地独立伸缩其中的一部分。

在编码层次,我们无时不刻都在运用这条原则。JAR文件、包、Bundle等等,都是用来隔离和抽象功能的机制。

在应用层次,eBay将不同的功能划分成几个应用程序池。销售功能由一组应用服务器运行,投标功能由另一组负责,搜索又是另外一组服务器。我们把总 共约16,000台应用服务器分成220个池。这样就可以根据某项功能的资源消耗,单独地伸缩其中一个池。我们也因此得以进一步隔离及合理化资源依赖关系 ——比如销售池只需要访问后台资源的一个相对较小的子集。

在数据库层次,我们也采取同样的做法。eBay没有无所不包的单一数据库,相反我们有一组数据库主机存放用户数据、一组存放商品数据、一组存放购买数据……总共1000个逻辑数据库分布在400台物理主机上。同样,这种做法让我们得以单独为某一类数据伸缩其数据库设施。

最佳实践 #2:水平切分

按功能分割对我们的帮助很大,但单凭它还不足以得到完全可伸缩的架构。即使将功能一一解耦,单项功能的资源需求随着时间增长,仍然有可能超出单一系 统的能力。我们常常提醒自己,“没有分割就没有伸缩”。在单项功能内部,我们需要能把工作负载分解成许多我们有能力驾驭的小单元,让每个单元都能维持良好 的性能价格比。这就是水平分割出场的时候了。

在应用层次,由于eBay将各种交互都设计成无状态的,所以水平分割是轻而易举之事。用标准的负载均衡服务器来路由进入的流量。所有应用服务器都是 均等的,而且任何服务器都不会维持事务性的状态,因此负载均衡可以任意选择应用服务器。如果需要更多处理能力,只需要简单地增加新的应用服务器。

数据库层次的问题比较有挑战性,原因是数据天生就是有状态的。我们会按照主要的访问路径对数据作水平分割(或称为“sharding”)。例如用户 数据目前被分割到20台主机上,每台主机存放1/20的用户。随着用户数量的增长,以及每个用户的数据量增长,我们会增加更多的主机,将用户分散到更多的 机器上去。商品数据、购买数据、帐户数据等等也都用同样的方式处理。用例不同,我们分割数据的方案也不同:有些是对主键简单取模(ID尾数为1的放到第一 台主机,尾数为二的放到下一台,以此类推),有些是按照ID的区间分割(1-1M、1-2M等等),有些用一个查找表,还有些是综合以上的策略。不过具体 的分割方案如何,总的思想是支持数据分割及重分割的基础设施在可伸缩性上远比不支持的优越。

最佳实践 #3:避免分布式事务

看到这里,你可能在疑惑按功能划分数据和水平划分数据的实践如何满足事务要求。毕竟,几乎任何有意义的操作都要更新一个以上的实体——立即就可以举 出用户和商品的例子。正统的广为人知的答案是:建立跨资源的分布式事务,用两段式提交来保证要么所有资源全都更新,要么全都不更新。很不幸,这种悲观方案 的成本很可观。伸缩、性能和响应延迟都受到协调成本的反面影响,随着依赖的资源数量和客户数量的上升,这些指标都会以几何级数恶化。可用性亦受到限制,因 为所有依赖的资源都必须就位。实用主义的答案是,对于不相关的系统,放宽对它们的跨系统事务的保证。

左右逢源是办不到的。保证跨多个系统或分区之间的即时的一致性,通常既无必要,也不现实。Inktomi的Eric Brewer十年前提出的CAP公理是这样说的:分布式系统的三项重要指标——一致性(Consistency)、可用性(Availability)和 分区耐受性(Partition-tolerance)——在任意时刻,只有两项能同时成立。对于高流量的网站来说,我们必须选择分区耐受性,因为它是实 现可伸缩的根本。对于24×7运行的网站,选择可用性也是理所当然的。于是只好放弃即时一致性(immediate consistency)。

在eBay,我们绝对不允许任何形式的客户端或者分布式事务——因此绝不需要两段式提交。在某些经过仔细定义的情形下,我们会将作用于同一个数据库 的若干语句捆绑成单个事务性的操作。而对于绝大部分操作,单条语句是自动提交的。虽然我们故意放宽正统的ACID属性,以致不能在所有地方保证即时一致 性,但现实的结果是大部分系统在绝大部分时间都是可用的。当然我们也采用了一些技术来帮助系统达到最终的一致性(eventual consistency):周密调整数据库操作的次序、异步恢复事件,以及数据核对(reconciliation)或者集中决算(settlement batches)。具体选择哪种技术要根据特定用例对一致性的需求来决定。

对于架构师和系统的设计者来说,关键是要明白一致性并非“有”和“没有”的单选题。现实中大多数的用例都不要求即时一致性。正如我们经常根据成本和其他压力因素来权衡可用性的高低,一致性也同样可以量体裁衣,根据特定操作的需要而保证适当程度的一致性。

最佳实践 #4:用异步策略解耦程序

提高可伸缩性的另一项关键措施是积极地采取异步策略。如果组件A同步调用组件B,那么A和B就是紧密耦合的,而紧耦合的系统其可伸缩性特征是各部分 必须共同进退——要伸缩A必须同时伸缩B。同步调用的组件在可用性方面也面临着同样的问题。我们回到最基本的逻辑:如果A推出B,那么非B推出非A。也就 是说,若B不可用,则A也不可用。如果反过来A和B的联系是异步的,不管是通过队列、多播消息、批处理还是什么其他手段,它们就可以分别地伸缩。而且,此 时A和B的可用性特征是相互独立的——即使B受困或者死掉,A仍然能够继续前进。

整个基础设施从上到下都应该贯彻这项原则。即使在单个组件内部也可通过SEDA(分阶段的事件驱动架构,Staged Event-Driven Architecture)等技术实现异步性,同时保持一个易于理解的编程模型。组件之间也遵守同样的原则——尽可能避免同步带来的耦合。在多数情况下, 两个组件在任何事件中都不会有直接的业务联系。在所有的层次,把过程分解为阶段(stages or phases),然后将它们异步地连接起来,这是伸缩的关键。

最佳实践 #5:将过程转变为异步的流

用异步的原则解耦程序,尽可能将过程变为异步的。对于要求快速响应的系统,这样做可以从根本上减少请求者所经历的响应延迟。对于网站或者交易系统, 牺牲数据或执行的延迟时间(完成全部工作的实践)来换取用户的延迟时间(用户得到响应的时间)是值得的。活动跟踪、单据开付、决算和报表等处理过程显然都 应该属于后台活动。主要用例过程中常常有很多步骤可以进一部分解成异步运行。任何可以晚点再做的事情都应该晚点再做。

还有一个同等重要的方面认识到的人不多:异步性可以从根本上降低基础设施的成本。同步地执行操作迫使你必须按照负载的峰值来配备基础设施——即使在 任务最重的那一天里任务最重的那一秒,设施也必须有能力立即完成处理。而将昂贵的处理过程转变为异步的流,基础设施就不需要按照峰值来配备,只需要满足平 均负载。而且也不需要立即处理所有的请求,异步队列可以将处理任务分摊到较长的时间里,因而起到削峰的作用。系统的负载变化越大,曲线越多尖峰,就越能从 异步处理中得益。

最佳实践 #6:虚拟化所有层次

虚拟化和抽象化无所不在,计算机科学里有一句老话:所有问题都可以通过增加一个间接层次来解决。操作系统是对硬件的抽象,而许多现代语言所用的虚拟 机又是对操作系统的抽象。对象-关系映射层抽象了数据库。负载均衡器和虚拟IP抽象了网络终端。当我们通过分割数据和程序来提高基础设施的可伸缩性,为各 种分割增加额外的虚拟层次就成为重中之重。

在eBay,我们虚拟化了数据库。应用与逻辑数据库交互,逻辑数据库再按照配置映射到某个特定的物理机器和数据库实例。应用也抽象于执行数据分割的 路由逻辑,路由逻辑会把特定的记录(如用户XYZ)分配到指定的分区。这两类抽象都是在我们自己开发的O/R层上实现的。这样虚拟化之后,我们的运营团队 可以按需要在物理主机群上重新分配逻辑主机——分离、合并、移动——而完全不需要接触应用程序代码。

搜索引擎同样是虚拟化的。为了得到搜索结果,一个聚合器组件会在多个分区上执行并行的查询,但这个高度分割的搜索网格在客户看来只是单一的逻辑索引。

以上种种措施并不只是为了程序员的方便,运营上的灵活性也是一大动机。硬件和软件系统都会故障,请求需要重新路由。组件、机器、分区都会不时增减、 移动。明智地运用虚拟化,可使高层的设施对以上变化难得糊涂,你也就有了腾挪的余地。虚拟化使基础设施的伸缩成为可能,因为它使伸缩变成可管理的。

最佳实践 #7:适当地使用缓存

最后要适当地使用缓存。这里给出的建议不一定普遍适用,因为缓存是否高效极大地依赖于用例的细节。说到底,要在存储约束、对可用性的需求、对陈旧数 [...]

InfoQ 网站几篇不错的文章

数据库驱动应用程序中影响性能的反模式
关于ORMapping的一些负面因素的介绍, 个人认为当性能不是关键问题的时候, ORMapping 还是比较好的,可以实现快速的开发,快速部署, 由于其对数据库的封装,也带来部分问题, 牺牲掉数据库在连接,查询方面的效率..

Web开发必知的八种隔离级别
对ACID中的I(Isolation)的8种隔离级别的简单介绍,开发与数据库从业人员都有需要深入了解.

可伸缩性的最差实践
介绍可伸缩性架构设计时应该避免的诸多问题,如所有问题都依赖于一个工具集,如对资源的滥用..

一种正规的性能调优方法:基于等待的调优
类似于Oracle的Wait Interface Based优化,通过使用Instrument收集到系统的开销的详细信息, 并针对等待比较严重的部分进行优化,个人觉得具体可以参考Optimizing Oracle Performance里面的Rmethod优化方法.

构建的可伸缩性和达到的性能:一个虚拟座谈会
倾听多位资深架构师关于架构的分享..

关于性能与可伸缩性的概念.性能与可伸缩性息息相关,但是两个是完全不同的概念,性能更关乎体验,可伸缩性更关乎设计与部署

Randy:我同意独立于任何特定语言或框架来讨论伸缩性问题确实很有价值——不管实现策略如何,模式都是一样的。可是,首先让我们确保自己已经把性能和可伸缩性区分开了。性能是服务于一个单独请求时的资源使用问题。可伸缩性是当要服务更多(或更大)请求时资源消费量如何随之增长的问题。

它们是相关的,但是不是同一件事情:-)。幸运的是,许多改善其中一个问题的方法通常也会改善另一个问题。但是,速度非常快的系统可能并不是最具伸缩性的系统,反之亦然。

FiveRuns:我们关心性能、可伸缩性、可用性的对比。我们把性能定义为一个操作(或一些操作)如何快速地完成,比如,响应时间、每秒事件处理的数量等等;而可伸缩性——就是应用怎样很好地按比例扩大规模以应对更大的使用需求(比如,用户数、请求速度、数据容量)。

关于性能诊断,特别是紧急状况下的性能诊断,有几点基本要求,系统已经做好基本的Instrument,有比较好的Sence(通常要求对业务有很好的理解,有足够多的现场处理经验,以及相应的知识集)

Randy:通常我们会从注意到问题的地方开始,然后向前追溯。在应用栈的所有层次所进行的监测/勘测得越多,就越容易这样做。它还可以帮助有各种学科专长团队里的人们来检查这个问题。

性能因为更多关乎体验,关乎感觉,要很好的检测,就需要建立一些基本的基线, 是否达到标准,具体达到什么样的标准..

FiveRuns:我们得补充一下,适当地建立一个基线是很重要的:一个已知的好的性能表现。并且试图梳理出任何交织于最终用户功能瑕疵的性能问题——有时这些瑕疵看起来只和功能有关,但实际上也包含了性能问题。

Randy:性能绝对是基础。差的性能直接反映了底层基础架构的花销,以及用户的体验。这两者都是底线。一个性能低于预期的应用不是“完成”了的应用,一个缺乏必须功能的应用同样是没有完成的。

FiveRuns:感知的性能是最终用户感觉得到的,因此最应该修正。可是我认为感知的性能是很难测量的—— 即,它是依赖于上下文的,作为观察者,我们不能总是访问同一个上下文环境,而且我们不可能控制组成上下文的所有东西。

Randy:这两者都重要:-)。感知的性能影响了用户体验,而所谓的真实性能会影响成本。这最终就是一个业务决策:此刻最大的痛苦是什么,因此要看哪个相对更优先。

关于新技术的选择,不要盲目的跟进潮流,要仔细分析自己的业务,哪些是可以适用,哪些不能随便调整.

FiveRuns:作为这种产品的制造者,我们对这个问题特别感兴趣!我们认为它是好事——它鼓励创新并给我们带来新的思路。但是我们专注于使用最好的工具组合——新的不见得就是好的。

Randy:有选择是好事情。这取决于你怎么去选择。追赶潮流此刻不再比顽固坚持不能工作的东西更有生产效率。我要说的是在一个像eBay这样24×7都要运转的站点,我们在使用经证明是好的技术时需要非常小心。这是我们对公司的责任。在我们考虑引入任何新东西的时候,在我们考虑将其应用到站点之前都要经过非常广泛的评估。

Blaine:你只能通过使用软件实现伸缩性。“语言是不能伸缩的。框架是不能伸缩的。而架构是可以伸缩的。”

伸缩性是一个社会问题。协调你的业务、操作和工程师们是极其关键的,如果你在这方面不成功,你实现伸缩性的努力也会失败。如果你的公司不理解需要多方协调以培育成功,那么你也会失败。YouTube正是通过正确的处理这一社会问题实现了伸缩性。他们购买服务器、有一个好的投资计划、有足够的带宽。他们选择一件事情,把它做好,以组织的方式决策伸缩性,在天才工程师们的努力下,伸缩性如魔术般被实现了。

FiveRuns:性能改进/伸缩性提升是一件“容易”的事情——修正性能或伸缩性问题不总是像外界看得那么简单——很容易因此受到指责且很难真正解决“所有”给定的约束。以我们的经验,修正性能和伸缩性问题需要对技术和问题领域两方面都有深入了解和经验。

Randy:经常被曲解的是性能和可伸缩性这两者间的区别

架构师修炼之道
InfoQ几个编辑眼中的架构师. 涉及架构师的工作范围,架构师应该具备的技能与素质,个人感觉还是上次给出的51CTO的那个链接总结的更好一点..

赵劼:在我看来,一个合格的架构师需要具备开放的眼光,各种平台、系统、项目随手拈来皆可组合,唯一的目标则是针对合适的环境选择合适的做法,这显然需要在成本和质量之间进行权衡。作为一个架构师,应该具有很好的“弹性”,在真正的环境中,很少会遇见与过去一模一样的情况,因此也需要架构师能够大胆尝试,灵活应对,使用踏实而严谨的做法来进行推测。一个架构师也必须有着足够的沟通和交流能力,把自己的想法使用合适的方式告诉别人,并且根据别人的反馈进行不断调整自己的观点。没有东西是永远正确的,但是一个人往往会倾向于自己的结论,而作为一个合格的架构师,需要有能力认识到自己存在的缺陷,使用各种方式进行弥补。

宋玮:扩充知识面,学习了解众多技术及框架的特点和适用范围。了解非功能特性的相关技术和方法,包括可用性、容错性、可扩展性、可伸缩性等等;了解系统安全性方面的技术和框架以及系统性能和状态监测方面的知识及工具。除了技术方面,还架构师还应扩展自身的业务知识,不断提高业务分析能力。想要做到持续不断的学习,保持对各种技术、框架、产品的浓厚兴趣是必不可少的,另外还要掌握他们各自的优缺点及相应的适用场景。

李明:我觉得这个问题可以从两个方面去谈。一方面,架构师要紧跟技术潮流,了解技术的发展和趋势,利用新技术、新方法来提升团队的生产力,将技术转化为收益。…..。而另一方面,架构师要培养自己的专业领域。虽然从技术的层面上说,很多解决方案放之四海而皆准。但是,从业务的角度来说,很多行业的解决方案放到另外一个行业中,未必行得通。这就要求了架构师必须对所属行业的业务十分了解才行,这也是一个平日里需要修炼的地方。