zync-mzy / blog

repository as blog, issue as article
7 stars 1 forks source link

Dynamo: Amazon’s Highly Available Key-value Store(译文) #7

Open zync-mzy opened 4 years ago

zync-mzy commented 4 years ago

近期学习亚马逊Dynamo论文后,受益匪浅,于是想把文章翻译出来,一方面加深理解,另一方面也方便以后查阅

原文链接:https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf

zync-mzy commented 4 years ago

摘要

大规模场景下的可用性保障是我们在亚马逊网站——全球最大的电子商务运营平台之一——面临的最大的挑战之一;即使是最轻微的服务中断都会造成重大的经济损失,打击用户的信心。为全世界许多网站提供服务的亚马逊平台是搭建在数以万计的遍布全球的服务器和网络组件上的。在这种规模下,大大小小的组件服务连续不断地失败,在失败场景下管理持久状态的方法驱动着软件系统的可用性和扩展性设计。 本论文展示了Dynamo的设计和实现,一个被亚马逊的一些关键服务用于提供“永远在线”体验的高可用键值存储系统。为了达成这样的可用性标准,在一些失败的场景下Dynamo会牺牲一致性。它对对象版本控制和基于应用辅助的冲突解决进行了扩展,提供了新颖的接口供开发者使用。

zync-mzy commented 4 years ago

1. 介绍

亚马逊运营着一个面向全球的电子商务平台,在高峰情况下通过放置在世界各个数据中心的数万台服务器给数以千万记的消费者提供服务。亚马逊平台有很严格的运营要求,包括性能,可用性和效率,以及高可扩展性来应对平台业务的日益增长。可用性是最重要的指标之一,因为即使是最轻微的服务中断都会造成重大的金融后果和影响消费者的信任。除此之外,为了支持可持续增长,平台需要是高可扩展的。 我们从运营亚马逊平台中学习到的一课是,一个系统的可用性和扩展性取决于它的应用状态是如何被管理的。亚马逊使用的是一个去中心化的,松耦合的,面向服务的架构,提供了数百种服务。在这种环境中,能一直提供服务的存储技术是必不可少的。举例来说,无论是磁盘错误,还是网络抖动,甚至数据中心被龙卷风摧毁了,消费者都应该可以看到和添加物品到他们的购物车中。因此,管理购物车的服务需要总是能够读写数据存储模块中的数据,并且在多个数据中心都可以获取它的数据。 解决由数百万个组件组成的基础设施中的失败是我们的标准运作方式;无论什么时候,总是会有数量虽少但同样致命的服务器或网络组件无法工作。因此亚马逊的软件系统在构建的时候需要将失败处理当做常态,且不能影响可用性和性能。 为了满足可用性和扩展性的要求,亚马逊已经开发了一些存储技术,比如众所周知的Amazon Simple Storage Service(Amazon S3)。本文介绍的是另一种为亚马逊平台搭建的高可用和可扩展的分布式存储——Dynamo——的设计和实现。Dynamo是用来管理对高可用有要求且需要自主在可用性、一致性、成本和性能之间做出权衡的服务的状态的。亚马逊平台有很多不同的应用,对存储有不同的要求。其中有一些应用要求存储技术能让应用设计者基于上述指标的权衡关系来配置他们的数据存储,以代价最小的方式来达成高可用和保证性能。 亚马逊平台有很多服务只需要用主键来访问数据存储。对于许多服务来说,比如那些提供最佳卖家列表,购物车,消费者偏好,会话管理,销售榜,以及产品分类等的服务,一味地使用关系型数据库会导致效率低下,扩展性和可用性受限。Dynamo提供了一个简单的只支持主键访问的接口来应对这些应用的要求。 Dynamo综合了多种已知的技术来实现扩展性和可用性:通过一致性哈希来对数据进行分区和冗余,通过对象版本控制来实现一致性。在数据更新期间,副本的一致性是通过一种类似投票的技术和去中心化复制同步协议来维护的。Dynamo采用一种基于留言的分布式错误检测和成员协议。Dynamo是一种完全去中心化的系统,只需要极少的人为管理。无需任何人为分区和重新部署就可以往Dynamo集群里面添加和移除存储节点。 在过去的几年里,Dynamo已经成为了亚马逊电商平台许多关键服务的底层存储技术。在繁忙的假期购物季,它能够在不停服的情况下高效地扩容来支持高负载。比如,为数千万请求提供服务的购物车服务一天处理超过三百万次结账,管理会话状态的服务要并行处理成千上百的活跃会话。 这篇文章对调研社区最主要的贡献是评估了如何结合不同的技术来组成一个高可用的系统。它证明了一个提供最终一致性保证的存储系统是可以在生产中用于要求严苛的应用中的。它还展示了如何对这些技术进行调整来满足对性能有严格要求的生产系统的需要。 文章结构如下。第二节陈述背景,第三节介绍相关工作。第四节描述系统设计,第五节讲解实现细节。第六节会分享在生产环境运营Dynamo的经验和收获,第七节进行总结。本文中有一些地方应该提供更多信息,但为了保护亚马逊的商业利益我们只能隐藏一些细节。因此,对于第6节中跨数据中心和数据中心内的延迟,6.2节中的请求速率和6.3节中不可用时长和负载,文中给出的只是统计数据,而无法提供更细致的数据。

zync-mzy commented 4 years ago

2. 背景

亚马逊电商平台提供了数百种服务,它们相互协作提供了从推荐系统到订单填写再到错误检测等功能。每一种服务都暴露了定义良好的接口,可以通过网络访问。这些服务是搭建在一定的基础设施上的,包含了成千上万的服务器,部署在世界各地的数据中心里面。其中,有些服务是无状态的(比如用于汇聚其他服务响应的服务),有些是有状态的(比如依赖持久化存储里的状态进行业务逻辑计算后生成响应的服务)。 按照传统,生产系统一般会把它们的状态存储在关系型数据库中。然而,从大多数对状态持久化的使用模式上看,关系型数据库是一个很不完美的方案。很多服务只是需要通过主键来存储和获取数据,用不到RDBMS提供的复杂的查询和管理功能。这些额外的功能需要昂贵的硬件支持和有经验的人来操作,是一个很低效的方案。除此之外,这种方案能使用的副本技术非常有限,而且往往会牺牲可用性来保证一致性。尽管这些年来已经做了许多优化,对数据库进行扩容和使用智能分区方案来做负载均衡依旧不是一件容易的事。 本文介绍了Dynamo,一种高可用的数据存储技术,来满足这些重要服务的需求。Dynamo提供了简单的键/值接口,在满足指定的可用性标准的同时保证高可用,能充分利用资源,并提供了简单的扩容方案来应对数据量或请求量的增长。每一个使用Dynamo的服务上运行的都是单独的Dynamo实例的。

2.1 系统假定和要求

这类服务对存储系统有如下的要求: 请求模型:每个数据项都有与之对应的一个唯一标识的键,可以用来进行简单的读写操作。状态是被当做二进制对象保存的,并且能通过一个唯一的键来标识。没有跨越多个数据项的操作,不需要关系型的语法。提出这个要求是因为我们观察到亚马逊的相当一部分服务就是在这种简单的请求模型下工作的,并不需要任何关系型的语法。Dynamo的受众是那些只需要进行小数据存储(通常情况下小于1MB)的应用。 ACID属性:ACID(原子性,一致性,隔离性,持久性)是用来保证数据库事务被可靠执行的一系列属性。在数据库领域,对数据的单一逻辑操作称为一个事务。亚马逊的经验表明,提供ACID保证的数据存储的可用性一般都比较差。这一认知在工业界和学术界也是被广泛认可的。Dynamo弱化了一致性为应用带来高可用性。Dynamo不提供隔离性保证,只允许单一键的更新。 效率:系统需要能够在商用硬件设施上面工作。在亚马逊平台,服务有严格的延迟要求,一般用分布中第99.9的百分位来衡量(译注:应该是指99.9%的请求要满足延迟要求)。鉴于状态访问在服务操作中起着至关重要的作用,存储系统必须能够满足如此严格的SLA。服务要能够配置Dynamo来持续地达到延迟和吞吐量要求。需要权衡的有性能,成本,可用性和持久性保证。 其他假定:Dynamo只用于亚马逊的内部服务。它的操作环境被假定为不受攻击的,因此没有鉴权授权等安全性要求。除此之外,由于每个服务都有单独的Dynamo实例,Dynamo的设计只考虑到扩容到几百台存储主机的情况。我们会在后面的章节讨论Dynamo的扩缩容限制和相关的展开内容。

2.2 服务水平协议(SLA

为了保证应用能够在规定时间内完成响应,它依赖的其他平台模块需要在更严格的时间内完成响应。客户端和服务端用到了服务水平协议(SLA),一种客户端和服务端在某些系统特性方面达成一致的正式的合约,通常包括客户端预期对某个API的请求率分布以及对服务端在这些条件下的延迟的期待值。一个简单的SLA例子是,一个服务保证在最高每秒500次请求的情况下99.9%的请求能够在300ms内得到响应。 在亚马逊这种面向去中心化服务的基础设施中,SLA扮演了重要的角色。比如对电商页面的一个页面请求一般要求渲染引擎请求查过150个服务来组成响应内容。这些服务经常对多个其他服务也有依赖,所以一个应用的调用关系图一般都是多个层级的。为了保证页面渲染引擎能够在给定的时间内完成页面响应,调用链上的每个服务都必须遵守它的性能合约。

image

表一提供了亚马逊平台架构的抽象视图,其中网页的动态内容是由页面渲染组件请求其他的许多服务所生成的。一个服务可以用不同的数据存储来管理它的状态,这些数据存储只能在服务边界内被访问到。一些服务会聚合其他服务来提供综合的响应。通常这种聚合服务是无状态的,尽管它们用到大量的缓存。 一般业界用均值,中位数和方差来描述面向性能的SLA。在亚马逊我们发现这些指标不够好,因为我们的目标是构建一个让所有消费者都体验良好的系统,而不只是给大多数。比如如果使用了粗放的个性化技术的话,历史比较多的消费者需要更多的处理时间,这影响的是分布中高位的表现(译注:这里应该是想强调,如果以处理时长为横坐标来描绘请求分布的话,这些消费者会落在时长较长的那端)。用响应时长的均值或者中位数做SLA的话,无法体现这段区间内的消费者的性能(译注:即这些消费者的体验)。为了解决这个问题,在亚马逊,SLA是用99.9%的分布情况来表达和衡量的。选择99.9%而不是更高的百分比是基于成本收益分析决定的,要把性能继续提高的话会导致非常大的开销。亚马逊生产系统的经验表明,这种方式相对于那些用均值和中位数来定义SLA的系统来说,提供了更好的体验。 文中有很多地方用到了这种99.9%的分布,反映了亚马逊工程师用消费者体验的视角不懈专注于性能。很多论文报告的是平均数,出于对比的目的,本文也包含了平均数。然而,亚马逊的工程和优化并不是聚焦于平均数的。诸如基于负载均衡选取写协调员的技术是完全以保证99.9%的表现为目标的。 存储系统在发布一个服务的SLA的过程中经常扮演一个重要的角色,特别是当业务逻辑相对较轻的情况,而许多亚马逊的服务都是这种情况。状态管理成为了一个服务的SLA的主要部分。Dynamo的一个主要的设计考虑就是让服务来控制它们系统的属性,比如持久性和一致性,让服务自行权衡功能性,性能和成本。

2.3 设计考虑

商业系统用到的数据副本算法一般是同步复制协调的,以保证强一致的数据访问接口。为了实现这种级别的一致性,这些算法在一些失败场景下只能损失可用性。举例来说,数据在被确认正确之前是无法访问的,而不是确定答案的正确性。从很早的有副本的数据库作品中得知,在处理可能的网络失败的时候,强一致性和高可用性是无法同时满足的。因此系统和应用必须清楚哪种属性能够在哪种条件下实现。 对于服务器和网络很容易失败的系统来说,能够通过乐观副本技术来提高可用性,这意味着允许修改在后台推送给其他副本,并行的和失联的工作也是允许的。这种方案的挑战点在于,它会导致修改的冲突,必须检测和解决这种冲突。解决冲突涉及两个问题:什么时候解决以及谁来解决。Dynamo被设计为一个最终一致的数据存储;也就是说所有的更新最终都会到达所有副本(而不是同时到达)。 需要慎重考虑的一个问题是,何时对更新冲突进行处理,比如,是在读取还是写入的时候解决冲突。很多传统的数据存储在写入时解决冲突来保持读取的简洁。在这些系统里,如果数据无法在规定时间内发送到所有(或大部分)副本的话,写入有可能被拒绝。反过来,Dynamo的目标是“永远可写”的数据存储(比如一个高可写的数据存储)这一块的设计空白。对亚马逊相当一部分的服务来说,拒绝消费者的修改会导致糟糕的用户体验。举例来说,购物车服务必须允许消费者添加和移除物品,即使这之中发生了网络和服务器故障。这样的要求迫使我们将解决冲突的复杂性放到读取上面,来保证写入永远不会被拒绝。 另一个设计选择是谁来解决冲突。这既可以让数据存储也可以让应用来完成。如果让数据存储来完成的话,它的选择相当有限。这种情况下,数据存储只能使用简单的策略,比如“最后写入覆盖”,来解决更新冲突。另一方面,由于应用知道它的数据用途,它可以决定用哪种方法解决冲突能带来最好的用户体验。比如,维护消费者购物车的应用可以选择“合并”冲突并返回一个聚合后的购物车。尽管这很灵活,一些应用开发者可能不想要自行开发冲突解决机制,而把问题抛给数据存储,这相当于选择一些简单的策略,比如“最后写入覆盖”。 设计中的其他关键原则还包括: 增量扩容:Dynamo应该能够一次扩容一台存储服务器(后续用“节点”代替),且尽量不影响系统和系统的操作者。 对称性:Dynamo的每一个节点都是等价的,提供相同的服务;没有节点是不同的或具备特殊身份或具备额外的职责。从我们的经验来看,对称性简化了系统的运营和维护。 去中心化:对称性的一个扩展,设计更青睐去中心化的点到点的技术,而不是中心化的控制。过去,中心化的控制导致了停服,现在的目标是尽量避免这种情况的发生。这让系统变得更加简单,更易于扩展,和d加可用。 异质性:系统要能够利用好基础设施的异质性。比如,工作负载要成比例地分布到具备不同能力的服务器上。这很有必要,它能让我们添加新的不同规格的节点,而不需要同时升级所有主机。

zync-mzy commented 4 years ago

3. 相关工作

3.1 点对点系统

已经有一些点对点(P2P)系统着手数据存储和分布的问题了。像FreenetGnutella这类第一代的P2P系统主要被用于文件分享系统。有一些非结构化P2P网络,节点之间的连接是随意建立的。在这种网络中,一次查询请求经常会在整个网络里泛洪,在尽量多的节点上查到数据。后来P2P系统发展到下一代,就是广为人知的结构化P2P网络。这种网络使用了一个全局的一致性协议来保证任何一个节点都能够把一次查询请求有效地路由到持有请求数据的节点上。PastryChord之类的系统使用了一些路由机制来保证请求能够在指定跳数之内得到响应。为了减少多跳带来的额外延迟,一些P2P系统使用O(1)复杂度的路由策略。通过在每个节点维护足够多的路由信息,对某个数据的请求就能够在指定跳数内被路由到适当的节点上。 很多存储系统,像OceanstorePAST,就是构建在这些路由覆盖上面的。Oceanstore提供了一个全局的,事务性的,持久的存储服务,支持在大量副本数据上进行序列化的更新。为了避免在并行更新时由于大粒度加锁带来太多问题,它使用了一种基于冲突解决的更新模型。冲突解决是用来减少事务被中止的次数的。Oceanstore通过序列化更新来解决冲突,通过确定好所有的更新顺序,有序地进行更新。它是为那些数据在不可靠的基础设施上复制的环境(译注:即数据复制的环境不可靠)准备的。相对的,PAST基于Pastry提供了一个抽象层,用于处理持久的不可改变的对象。它假定应用可以在它之上构建所需的存储语义(比如可改变的文件)。

3.2 分布式文件系统和数据库

分发数据以提高性能,可用性和持久性的工作,已经在文件系统和数据库系统社区中被广泛研究过了。相较于P2P这样只支持扁平命名空间的存储系统,分布式文件系统一般支持分层命名空间。FicusCoda这类系统在复制数据的时候,以一致性为代价来换取高可用性。更新冲突一般会由专门的冲突处理流程来管理。Farsite系统是一个没有像NFS一样的中心服务器的分布式文件系统。它用副本的方式来实现高可用和可扩展。Google File System是为托管Google内部应用状态而创建的另一个分布式文件系统。GFS用了一个简单的设计,它有一个主服务器托管了全部的元数据,而数据则被分块并保存到了块服务器上。Bayou是一个支持离线操作和提供最终一致性的分布式关系型数据库系统。 在这些系统当中,BayouCodaFicus支持离线操作,并且能够容忍对网络分区和离线。这三个系统主要是在冲突处理流程上有差异。举例来说,CodaFicus在系统级别解决冲突,而Bayou则在应用级别解决。但是它们全都保证了最终一致性。与这些系统类似,Dynamo在网络分区的情况下也支持读写,并使用了不同的机制来解决更新冲突。像FAB之类的分布式块存储系统把大对象切分成了小块,用一种高可用的方式保存每一个小块。跟这种系统相比,键值存储会更加适合,因为:(a)它主要用于存储小对象(小于1M)以及(b)键值存储更易于针对每个应用进行配置。Antiquity是一个为处理多服务器失败而设计的应用广泛的分布式存储系统。它使用了安全日志来保证数据的合法,把日志复制到多个服务器上来保证持久性,用拜占庭容错协议来保证数据的一致性。跟它相比,Dynamo不关心数据合法性和安全性的问题,它是用于可信任的环境里的。Bigtable是用来管理结构化数据的分布式存储系统。它维护了一个松散的多维的有序字典,允许应用通过多种属性来获取他们的数据。跟Bigtable相比,Dynamo面向的是只需要通过主键来获取数据的应用, 关注的是高可用性,即使在发生网络分区或者服务器故障的时候也不会拒绝(数据)更新。 传统的有副本的关系型数据库聚焦于保证所有副本数据的强一致性。尽管强一致性给了应用开发者一个很方便的编程模型,这类系统往往在扩展性和可用性方面存在诸多限制。这类系统无法处理网络分区问题,因为他们通常要保证强一致性。

3.3 讨论

Dynamo面向的需求跟之前提到的去中心化的存储系统是不一样的。首先,Dynamo主要是面向那些要求数据存储“永远可写”的应用的,更新请求不会由于写失败或者并发写而被拒绝。这是亚马逊很多应用的关键需求。第二,如早些时候提到的,Dynamo是为处于单一管理域内的基础设施所设计的,里面的所有节点都是可信赖的。第三,使用Dynamo的应用不需要用到分层命名空间(这是很多文件系统的规范之一)和复杂的关系型语法(传统数据库的特性)。第四,Dynamo是为对延迟敏感的应用建设的,至少99.9%的读写操作都要在数百毫秒内完成。为了满足这种严格的延迟要求,我们势必要避免在多个节点之间路由请求(这是ChordPastry等分布式哈希表系统通常会采用的设计)。这是因为多跳路由会给响应时间添加变数,从而增加较高百分位数的延迟(译注:可能是想说增加了原本延迟就较高的请求的延迟,因为延迟较高的请求一般跳数也较多)Dynamo可以被当做是一个零跳的DHT(译注:Distributed Hash Table,分布式哈希表),每个节点都在本地维护了足够多的路由信息以便把请求直接路由到合适的节点上去。

zync-mzy commented 4 years ago

4. 系统架构

一个要在生产设定下运作的存储系统的架构是很复杂的。除了数据持久化组件之外,系统还要有可扩展的鲁棒的负载均衡方案,成员和错误检测,错误恢复,副本同步,过载处理,状态转移,并发数和任务调度,请求编码,请求路由,系统监控告警,以及配置管理。挨个方案描述是不可能的,因此本文会聚焦在Dynamo用到的核心的分布式技术:分区,副本,版本控制,成员,错误处理和扩容。

image

4.1 系统接口