通过一个大型项目来学习分布式算法(1)

Dynamo:Amazon的高可用性的键-值存储系统

摘要 

巨大规模系统的可靠性是我们在Amazon.com,这个世界上最大的电子商务公司之一,面对最大的挑战之一,即使最轻微的系统中断都有显着的经济后果并且影响到客户的信赖。Amazon.com平台,它为全球许多网站服务,是实现在位于世界各地的许多数据中心中的成千上万的服务器和网络基础设施之上。在这一规模中,各种大大小小的部件故障持续不断发生,管理持久化状态的方法在面对这些故障时,驱使软件系统的可靠性和可扩展性。

本文介绍Dynamo的设计和实现,一个高度可用的key-value存储系统,一些Amazon的核心服务使用它用以提供一个“永远在线”的用户体验。为了达到这个级别的可用性,Dynamo在某些故障的场景中将牺牲一致性。它大量使用对象版本和应用程序协助的冲突协调方式以提供一个开发人员可以使用的新颖接口。

1简介 

Amazon运行一个全球性的电子商务服务平台,在繁忙时段使用位于世界各地的许多数据中心的数千台服务器为几千万的客户服务。Amazon平台有严格的性能,可靠性和效率方面操作要求,并支持持续增长,因此平台需要高度可扩展性。可靠性是最重要的要求之一,因为即使最轻微的系统中断都有显著的经济后果和影响客户的信赖。此外,为了支持持续增长,平台需要高度可扩展性。

我们组织在运营Amazon平台时所获得的教训之一是,一个系统的可靠性和可扩展性依赖于它的应用状态如何管理。Amazon采用一种高度去中心化(decentralized),松散耦合,由数百个服务组成的面向服务架构。在这种环境中特别需要一个始终可用存储技术。例如,即使磁盘故障,网络路线状态摇摆不定,或数据中心被龙卷风摧毁时,客户应该能够查看和添加物品到自己的购物车。因此,负责管理购物车的服务,它可以随时写入和读取数据,并且数据需要跨越多个数据中心。

在一个由数百万个组件组成的基础设施中进行故障处理是我们的标准运作模式;在任何给定的时间段,总有一些小的但相当数量的服务器和网络组件故障,因此Amazon的软件系统需要将错误处理当作正常情况下来建造,而不影响可用性或性能。

为了满足可靠性和可伸缩性的需要,Amazon开发了许多存储技术,其中Amazon简单存储服务(一个Amazon之外也可获得的服务,即广为人知的AmazonS3SimpleStorageService)大概是最为人熟知。本文介绍了Dynamo的设计和实现,另一个为Amazon的平台上构建的高度可用和可扩展的分布式数据存储系统。Dynamo被用来管理服务状态并且要求非常高的可靠性,而且需要严格控制可用性,一致性,成本效益和性能的之间的权衡。Amazon平台的不同应用对存储要求非常差异非常高。一部分应用需要存储技术具有足够的灵活性,让应用程序设计人员配置适当的数据存储来达到一种平衡,以实现高可用性和最具成本效益的方式保证性能。

Amazon服务平台中的许多服务只需要主键访问数据存储。对于许多服务,如提供最畅销书排行榜,购物车,客户的偏好,会话管理,销售等级,产品目录,常见的使用关系数据库的模式会导致效率低下,有限的可扩展性和可用性。Dynamo提供了一个简单的主键唯一的接口,以满足这些应用的要求。

Dynamo综合了一些著名的技术来实现可伸缩性和可用性:数据划分(data partitioned)和使用一致性哈希的复制(replicated)[10],并通过对象版本(object versioning)提供一致性[12]。在更新时,副本之间的一致性是由仲裁般(quorum-like)的技术和去中心化的副本同步协议来维持的。Dynamo采用了基于gossip(不知道怎样译过来好,流言蜚语?或许保留成外来语为好)的分布式故障检测及成员(membership)协议(也即token环上的节点在响应节点加入(join)/离开(leaving)/移除(removing)/消亡(dead)等所采取的动作以维持DHT/Partitioning的正确语义)。Dynamo是一个只需要很少的人工管理,去中心化的系统。存储节点可以添加和删除,而不需要任何手动划分(partitioning - partitioner controls how the data are distributed over the nodes)或重新分配(redistribution)。

在过去的一年,Dynamo已经成为Amazon电子商务平台的核心服务的底层存储技术。它能够有效地扩展到极端高峰负载,在繁忙的假日购物季节没有任何的停机时间。例如,维护购物车(购物车服务)的服务,在一天内承担数千万的请求,并因此导致超过300万checkouts(结算?),以及管理十万计的并发活动会话的状态。

这项工作对研究社区的主要贡献是评估不同的技术如何能够结合在一起,并最终提供一个单一的高可用性的系统。它表明一个最终一致(eventually-consistent)的存储系统可以在生产环境中被苛刻的应​​用程序所采用。它也对这些技术的调整的进行深入的分析,以满足性能要求非常严格的生产系统的需求。

本文的结构如下:第2节背景知识,第3节阐述有关工作,第4节介绍了系统设计,第5描述了实现,第6条详述经验和在生产运营Dynamo时取得的经理,第7节结束本文。在这其中,有些地方也许可以适当有更多的信息,但出于适当保护Amazon的商业利益,要求我们在文中降低详细程度。由于这个原因,第6节数据中心内和跨数据中心之间的延时,第6.2节的相对请求速率,以及第6.3节系统中断(outage)的时长,系统负载,都只是总体测量而不是绝对的详细。

2背景

Amazon电子商务平台由数百个服务组成,它们协同工作,提供的功能包括建议,完成订单到欺诈检测。每个服务是通过一个明确的公开接口,通过网络访问。这些服务运行在位于世界各地的许多数据中心的数万台服务器组成的基础设施之上。其中一些服务是无状态(比如,一些服务只是收集(aggregate)其他服务的response),有些是有状态的(比如,通过使用持久性存储区中的状态,,执行业务逻辑,进而生成response)。

传统的生产系统将状态存储在关系数据库中。对于许多更通用的状态存储模式,关系数据库方案是远不够理想。因为这些服务大多只通过数据的主键存储和检索数据,并且不需要RDBMS提供的复杂的查询和管理功能。这多余的功能,其运作需要昂贵的硬件和高技能人才,使其成为一个非常低效的解决方案。此外,现有的复制(replication)技术是有限的,通常选择一致性是以牺牲可用性为代价(typically choose consistency over availability)。虽然最近几年已经提出了许多进展,但数据库水平扩展(scaleout)或使用负载平衡智能划分方案仍然不那么容易。

本文介绍了Dynamo,一个高度可用的数据存储技术,能够满足这些重要类型的服务的需求。Dynamo有一个简单的键/值接口,它是高可用的并同时具有清晰定义的一致性滑动窗口,它在资源利用方面是高效的,并且在解决规模增长或请求率上升时具有一个简单的水平扩展(scaleout)方案。每个使用Dynamo的服务运行它自己的Dynamo实例。

2.1系统假设和要求 

这种类型的服务的存储系统具有以下要求:

查询模型:对数据项简单的读,写是通过一个主键唯一性标识。状态存储为一个由唯一性键确定二进制对象(即blob,呵呵,java就是ByteBuffer啦)。没有横跨多个数据项的操作,也不需要关系方案(relational schema)。这项规定是基于观察,相当一部分Amazon的服务可以使用这个简单的查询模型,并不需要任何关系模式。Dynamo的目标应用程序需要存储的对象都比较小(通常小于1MB)。

ACID属性:ACID(原子性,一致性,隔离性,持久性)是一种保证数据库事务可靠地处理的属性。在数据库方面的,对数据的单一的逻辑操作被称作所谓的交易。Amazon的经验表明,在保证ACID的数据存储提往往有很差的可用性。这已被业界和学术界所公认[5]。Dynamo的目标应用程序是高可用性,弱一致性(ACID中的“C”)。Dynamo不提供任何数据隔离(Isolation)保证,只允许单一的关键更新。

效率:系统需运作在“日用品”(commodity,非常喜欢这个词,因为可以在家做试验!)级的硬件基础设施上。Amazon平台的服务都有着严格的延时要求,一般延时所需要度量到分布的99.9百分位。在服务操作中鉴于对状态的访问起着至关重要的作用,存储系统必须能够满足那些严格的SLA(见以下2.2),服务必须能够通过配置Dynamo,使他们不断达到延时和吞吐量的要求。因此,必须在成本效率,可用性和耐用性保证之间做权衡。

其他假设:Dynamo仅被Amazon内部的服务使用。它的操作环境被假定为不怀恶意的(non-hostile),没有任何安全相关的身份验证和授权的要求。此外,由于每个服务使用其特定的Dynamo实例,它的最初设计目标的规模高达上百的存储主机。我们将在后面的章节讨论Dynamo可扩展性的限制和相关可能的扩展性的延伸。

2.2服务水平协议(SLA) 

为了保证应用程序可以在限定的(bounded)时间内递送(deliver)其功能,一个平台内的任何一个依赖都在一个更加限定的时间内递送其功能。客户端和服务端采用服务水平协议(SLA),其为客户端和服务端在几个系统相关的特征上达成一致的一个正式协商合约,其中,最突出的包括客户对特定的API的请求速率分布的预期要求,以及根据这些条件,服务的预期延时。一个简单的例子是一个服务的SLA保证:在客户端每秒500个请求负载高峰时,99.9%的响应时间为300毫秒。

在Amazon的去中心化的面向服务的基础设施中,服务水平协议发挥了重要作用。例如,一个页面请求某个电子商务网站,通常需要页面渲染(rendering)引擎通过发送请求到150多个服务来构造其响应。这些服务通常有多个依赖关系,这往往是其他服务,因此,有一层以上调用路径的应用程序通常并不少见。为了确保该网页渲染引擎在递送页面时可以保持明确的时限,调用链内的每个服务必须履行合约中的性能指标。

图1显示了Amazon平台的抽象架构,动态网页的内容是由页面呈现组件生成,该组件进而查询许多其他服务。一个服务可以使用不同的数据存储来管理其状态,这些数据存储仅在其服务范围才能访问。有些服务作为聚合器使用其他一些服务,可产生合成(composite)响应。通常情况下,聚合服务是无状态,虽然他们利用广泛的缓存。

 

图1:面向服务的Amazon平台架构。

行业中,表示面向性能的SLA的共同做法是使用平均数(average),中值(median)和预期变化(expected variance)。在Amazon,我们发现,这些指标不够好,如果目标是建立一个对所有,而不是大多数客户都有着良好体验的系统。例如,如果个性化(personalization)技术被广泛使用,那么有很长的历史的客户需要更多的处理,性能影响将表现在分布的高端。前面所述的基于平均或中值响应时间的SLA不能解决这一重要客户段的性能问题。为了解决这个问题,在Amazon,SLA是基于分布的99.9百分位来表达和测量的。选择百分位99.9的而不是更高是根据成本效益分析,其显示出在99.9之后,要继续提高性能,成本将大幅增加。系统的经验与Amazon的生产表明,相比于那些基于平均或中值定义的SLA的系统,该方法提供了更好的整体体验。

本文多次提到这种99.9百分位分布,这反映了Amazon工程师从客户体验角度对性能不懈追求。许多论文统计平均数,所以在本论文的一些地方包括它可以用来作比较。然而,Amazon的工程和优化没有侧重于平均数。几种技术,如作为写协调器(coordinators)的负载均衡的选择,纯粹是针对控制性能在99.9百分位的。

存储系统在建立一个服务的SLA中通常扮演重要角色,特别是如果业务逻辑是比较轻量级时,正如许多Amazon的服务的情况。状态管理就成为一个服务的SLA的主要组成部分。对dynamo的主要设计考虑的问题之一就是给各个服务控制权,通过系统属性来控制其耐用性和一致性,并让服务自己在功能,性能和成本效益之间进行权衡。

2.3设计考虑 

在商业系统中,数据复制(Data replication)算法传统上执行同步的副本(replica)协调,以提供一个强一致性的数据访问接口。为了达到这个水平的一致性,在某些故障情况下,这些算法被迫牺牲了数据可用性。例如,与其不能确定答案的正确性与否,不如让该数据一直不可用直到它绝对正确时。从最早期的备份(replicated)数据库,众所周知,当网络故障时,强一致性和高可用性不可能性同时实现[2,11]。因此,系统和应用程序需要知道在何种情况下可以达到哪些属性。

对于容易出现的服务器和网络故障的系统,可使用乐观复制技术来提高系统的可用性,其变化可以在后台传播到副本,同时,并发和断开(disconnected)是可以容忍的。这种方法的挑战在于,它会导致更改冲突,而这些冲突必须检测并协调解决。这种协调冲突的过程引入了两个问题:何时协调它们,谁协调它们。Dynamo被设计成最终一致性(eventually consistent)的数据存储,即所有的更新操作,最终达到所有副本。

一个重要的设计考虑的因素是决定何时去协调更新操作冲突,即是否应该在读或写过程中协调冲突。许多传统数据存储在写的过程中执行协调冲突过程,从而保持读的复杂度相对简单[7]。在这种系统中,如果在给定的时间内数据存储不能达到所要求的所有或大多数副本数,写入可能会被拒绝。另一方面,Dynamo的目标是一个“永远可写”(always writable)的数据存储(即数据存储的“写”是高可用)。对于Amazon许多服务来讲,拒绝客户的更新操作可能导致糟糕的客户体验。例如,即使服务器或网络故障,购物车服务必须让客户仍然可向他们的购物车中添加和删除项。这项规定迫使我们将协调冲突的复杂性推给“读”,以确保“写”永远不会拒绝。

下一设计选择是执行协调冲突的过程。这可以通过数据存储或客户应用程序。如果冲突的协调是通过数据存储,它的选择是相当有限的。在这种情况下,数据存储只可能使用简单的策略,如”最后一次写入获胜“(last write wins)[22],以协调冲突的更新操作。另一方面,客户应用程序,因为应用知道数据方案,因此它可以基于最适合的客户体验来决定协调冲突的方法。例如,维护客户的购物车的应用程序,可以选择“合并”冲突的版本,并返回一个统一的购物车。尽管具有这种灵活性,某些应用程序开发人员可能不希望写自己的协调冲突的机制,并选择下压到数据存储,从而选择简单的策略,例如“最后一次写入获胜”。

设计中包含的其他重要的设计原则是:

增量的可扩展性:Dynamo应能够一次水平扩展一台存储主机(以下,简称为“节点“),而对系统操作者和系统本身的影响很小。

你可能感兴趣的:(算法,分布式事务)