亚马逊云计算的基础是基础设施服务,例如亚马逊的S3(简单存储服务)、SimpleDB和EC2(弹性计算云),这些服务为构建互联网规模的计算平台和各种应用程序提供了资源。对这些基础设施服务的要求非常严格;它们需要在安全性、可扩展性、可用性、性能和成本效益方面获得高分,并且需要在为全球数百万客户持续服务的同时满足这些要求。
在这些服务的背后是庞大的分布式系统,它们在全球范围内运行。这种规模带来了额外的挑战,因为当一个系统处理数万亿的请求时,通常发生概率较低的事件现在变得必然会发生,并且需要在系统的设计和架构中预先考虑到。鉴于这些系统的全球范围,我们普遍使用复制技术来保证一致的性能和高可用性。虽然复制使我们更接近目标,但它无法以完全透明的方式实现这些目标;在许多情况下,这些服务的客户将面临在服务内部使用复制技术的后果。
其中一种表现形式是所提供的数据一致性的类型,特别是当许多广泛分布的系统在数据复制的背景下提供最终一致性模型时。在亚马逊设计这些大规模系统时,我们使用一套与大规模数据复制相关的指导原则和抽象概念,并专注于高可用性和数据一致性之间的权衡。在本文中,我将介绍一些相关的背景知识,这些知识为我们交付需要在全球范围内运行的可靠分布式系统的方法提供了信息。本文的早期版本曾在 All Things Distributed 网络日志上发布,并在读者的帮助下得到了极大的改进。
在理想的世界中,应该只有一个一致性模型:当进行更新时,所有观察者都应该看到该更新。第一次发现这难以实现是在 70 年代后期的数据库系统中。关于这个主题的最佳“时代作品”是 Bruce Lindsay 等人的“分布式数据库笔记”5。它阐述了数据库复制的基本原则,并讨论了许多处理实现一致性的技术。这些技术中的许多都试图实现分布透明性——也就是说,对于系统的用户来说,它看起来好像只有一个系统,而不是多个协作系统。当时许多系统采取的方法是,与其破坏这种透明性,不如让整个系统失败2。
在 90 年代中期,随着更大规模互联网系统的兴起,这些实践被重新审视。那时,人们开始考虑可用性可能是这些系统最重要的属性,但他们一直在努力权衡它应该与什么进行权衡。加州大学伯克利分校的系统教授,当时也是 Inktomi 负责人的 Eric Brewer,在 2000 年的 PODC(分布式计算原理)会议的主题演讲中将不同的权衡结合在一起1。他提出了 CAP 定理,该定理指出,在共享数据系统的三个属性(数据一致性、系统可用性和网络分区容错性)中,在任何给定时间只能实现两个。Seth Gilbert 和 Nancy Lynch 在 2002 年的一篇论文中可以找到更正式的确认4。
不容忍网络分区的系统可以实现数据一致性和可用性,并且通常通过使用事务协议来实现。为了使之有效,客户端和存储系统必须是同一环境的一部分;在某些情况下,它们作为一个整体失败,因此,客户端无法观察到分区。一个重要的观察是,在更大规模的分布式系统中,网络分区是既定的事实;因此,一致性和可用性不能同时实现。这意味着有两种选择可以放弃:放宽一致性将允许系统在可分区的条件下保持高可用性,而将一致性作为优先事项意味着在某些条件下系统将不可用。
这两种选择都需要客户端开发人员意识到系统正在提供什么。如果系统强调一致性,开发人员必须处理系统可能不可用以进行写入的事实。如果由于系统不可用而导致此写入失败,则开发人员将不得不处理要写入的数据。如果系统强调可用性,它可能总是接受写入,但在某些情况下,读取将不会反映最近完成的写入的结果。然后,开发人员必须决定客户端是否需要始终访问绝对最新的更新。有一系列应用程序可以处理略微陈旧的数据,并且在这种模型下可以很好地为它们服务。
原则上,ACID 属性(原子性、一致性、隔离性、持久性)中定义的事务系统的一致性属性是一种不同类型的一致性保证。在 ACID 中,一致性与事务完成时数据库处于一致状态的保证有关;例如,当从一个帐户向另一个帐户转移资金时,两个帐户中的总金额不应改变。在基于 ACID 的系统中,这种一致性通常是编写事务的开发人员的责任,但可以由管理完整性约束的数据库来协助。
有两种看待一致性的方式。一种是从开发人员/客户端的角度来看:他们如何观察数据更新。第二种方式是从服务器端的角度来看:更新如何在系统中流动以及系统可以提供哪些关于更新的保证。
客户端一致性
客户端侧有以下组件
*最终一致性。 这是一种特殊的弱一致性形式;存储系统保证,如果没有对对象进行新的更新,最终所有访问都将返回最后更新的值。如果未发生故障,则可以根据诸如通信延迟、系统负载以及复制方案中涉及的副本数量等因素来确定不一致窗口的最大大小。实现最终一致性的最流行的系统是 DNS(域名系统)。对名称的更新根据配置的模式分发,并与时间控制的缓存相结合;最终,所有客户端都将看到更新。
最终一致性模型有许多重要的变体需要考虑
这些属性中的许多属性可以组合使用。例如,可以获得单调读取与会话级一致性的组合。从实践的角度来看,这两个属性(单调读取和读己之写)在最终一致性系统中是最理想的,但并非总是必需的。这两个属性使开发人员可以更轻松地构建应用程序,同时允许存储系统放宽一致性并提供高可用性。
从这些变体中可以看出,可能存在很多不同的场景。这取决于特定的应用程序是否可以处理这些后果。
最终一致性不是极端分布式系统的一些深奥属性。许多提供主备可靠性的现代 RDBMS(关系数据库管理系统)在其复制技术中同时实现了同步和异步模式。在同步模式下,副本更新是事务的一部分。在异步模式下,更新以延迟的方式到达备份,通常通过日志传送。在后一种模式下,如果主数据库在日志传送之前失败,则从提升的备份中读取将产生旧的、不一致的值。此外,为了支持更好的可扩展读取性能,RDBMS 已开始提供从备份读取的能力,这是提供最终一致性保证的经典案例,其中不一致窗口取决于日志传送的周期性。
在服务器端,我们需要更深入地了解更新如何在系统中流动,以了解是什么驱动了使用该系统的开发人员可以体验到的不同模式。在开始之前,让我们建立一些定义
N = 存储数据副本的节点数
W = 在更新完成之前需要确认收到更新的副本数
R = 通过读取操作访问数据对象时联系的副本数
如果 W+R > N,则写入集和读取集始终重叠,并且可以保证强一致性。在实现同步复制的主备 RDBMS 场景中,N=2、W=2 和 R=1。无论客户端从哪个副本读取,它都将始终获得一致的答案。在使用从备份读取的异步复制中,N=2、W=1 和 R=1。在这种情况下,R+W=N,并且无法保证一致性。
这些配置(它们是基本的仲裁协议)的问题是,当系统由于故障而无法写入 W 个节点时,写入操作必须失败,从而标记系统的不可用性。当 N=3 和 W=3 且只有两个节点可用时,系统将不得不使写入失败。
在需要提供高性能和高可用性的分布式存储系统中,副本的数量通常高于两个。仅关注容错的系统通常使用 N=3(使用 W=2 和 R=2 配置)。需要服务于非常高的读取负载的系统通常会将其数据复制到超出容错所需的范围;N 可以是数十甚至数百个节点,R 配置为 1,以便单个读取将返回结果。关注一致性的系统将 W 设置为 N 以进行更新,这可能会降低写入成功的概率。对于这些关注容错但不关注一致性的系统,一种常见的配置是以 W=1 运行以获得更新的最小持久性,然后依赖于惰性(流行病式)技术来更新其他副本。
如何配置 N、W 和 R 取决于常见情况是什么以及需要优化哪个性能路径。在 R=1 和 N=W 中,我们针对读取情况进行优化,而在 W=1 和 R=N 中,我们针对非常快速的写入进行优化。当然,在后一种情况下,在出现故障时无法保证持久性,并且如果 W < (N+1)/2,则当写入集不重叠时,可能会发生冲突的写入。
当 W+R <= N 时,会出现弱/最终一致性,这意味着读取集和写入集可能不会重叠。如果这是一个有意的配置而不是基于故障情况,那么将 R 设置为除 1 以外的任何值几乎没有意义。这发生在两种非常常见的情况下:第一种是前面提到的用于读取扩展的大规模复制;第二种是数据访问更复杂的情况。在简单的键值模型中,很容易比较版本以确定写入系统的最新值,但是在返回对象集的系统中,更难确定什么是正确的最新集。在大多数写入集小于副本集的系统中,都有一种机制可以以惰性的方式将更新应用于副本集中剩余的节点。所有副本都已更新的期间是之前讨论的不一致窗口。如果 W+R <= N,则系统容易从尚未收到更新的节点读取。
是否可以实现读己之写、会话和单调一致性通常取决于客户端对于为其执行分布式协议的服务器的“粘性”。如果每次都是同一台服务器,那么保证读己之写和单调读取就相对容易。这使得管理负载平衡和容错变得稍微困难,但这是一个简单的解决方案。使用会话(它是粘性的)使这一点变得明确,并提供了客户端可以推理的暴露级别。
有时客户端实现读己之写和单调读取。通过在写入时添加版本,客户端会丢弃版本早于上次看到的版本的读取值。
当系统中的某些节点无法到达其他节点时,就会发生分区,但是两组节点都可以被客户端组访问。如果使用经典的多数仲裁方法,则副本集中具有 W 个节点的分区可以继续接受更新,而另一个分区变得不可用。读取集也是如此。鉴于这两个集合重叠,根据定义,少数集合变得不可用。分区不会经常发生,但它们确实会在数据中心之间以及数据中心内部发生。
在某些应用程序中,任何分区的不可用性都是不可接受的,重要的是可以到达该分区的客户端取得进展。在这种情况下,双方都分配一组新的存储节点来接收数据,并在分区恢复时执行合并操作。例如,在亚马逊内部,购物车使用这样的始终写入系统;在发生分区的情况下,即使原始购物车位于其他分区上,客户也可以继续将商品放入购物车。购物车应用程序协助存储系统在分区恢复后合并购物车。
亚马逊的 Dynamo 系统将所有这些属性都置于应用程序架构的显式控制之下,Dynamo 是一个键值存储系统,在构成亚马逊电子商务平台的许多服务以及亚马逊的 Web 服务中内部使用。Dynamo 的设计目标之一是允许创建 Dynamo 存储系统实例(通常跨越多个数据中心)的应用程序服务所有者在一定的成本点上权衡一致性、持久性、可用性和性能3。
大规模可靠分布式系统中的数据不一致性必须被容忍有两个原因:在高度并发条件下提高读取和写入性能;以及处理分区情况,在这些情况下,即使节点已启动并正在运行,多数模型也会使系统的一部分不可用。
不一致性是否可以接受取决于客户端应用程序。在所有情况下,开发人员都需要意识到存储系统提供的一致性保证,并且在开发应用程序时需要考虑到这些保证。最终一致性模型有很多实际的改进,例如会话级一致性和单调读取,这些改进为开发人员提供了更好的工具。很多时候,应用程序能够处理存储系统的最终一致性保证而没有任何问题。一个特定的流行案例是一个网站,在其中我们可以具有用户感知的一致性的概念。在这种情况下,不一致窗口需要小于客户返回以进行下一次页面加载的预期时间。这允许更新在预期下一次读取之前在系统中传播。
本文的目标是提高人们对工程系统的复杂性的认识,这些系统需要在全球范围内运行,并且需要仔细调整以确保它们可以交付其应用程序所需的持久性、可用性和性能。系统设计人员拥有的工具之一是不一致窗口的长度,在此期间,系统的客户端可能会暴露于大规模系统工程的现实。
WERNER VOGELS 是 Amazon.com 的副总裁兼首席技术官,他负责推动公司的技术愿景,即代表亚马逊全球客户不断增强创新能力。在加入亚马逊之前,他曾在康奈尔大学担任研究科学家,在那里他是多个研究项目的主要研究员,这些项目针对任务关键型企业计算系统的可扩展性和鲁棒性。他曾在处理学术技术向工业转型的公司担任技术副总裁和首席技术官职位。Vogels 拥有阿姆斯特丹自由大学的博士学位,并为期刊和会议撰写了近 80 篇文章,其中大部分文章是关于企业计算的分布式系统技术。
最初发表于 Queue vol. 6, no. 6—
在 数字图书馆 中评论本文
Niklas Blum, Serge Lachapelle, Harald Alvestrand - WebRTC - 开放 Web 平台的实时通信
在这个疫情时期,世界比以往任何时候都更依赖于基于互联网的 RTC(实时通信)。在过去十年中,RTC 产品的数量呈爆炸式增长,这在很大程度上是由于更便宜的高速网络接入和更强大的设备,同时也归功于一个名为 WebRTC 的开放、免版税平台。WebRTC 正在从实现有用的体验发展成为在疫情期间让数十亿人继续工作和教育,并保持重要的人际接触的关键。WebRTC 未来的机遇和影响确实令人着迷。
Benjamin Treynor Sloss, Shylaja Nukala, Vivek Rau - 重要的指标
衡量您的站点可靠性指标,设定正确的目标,并努力准确地衡量这些指标。然后,您会发现您的服务运行得更好,停机时间更少,并且用户采用率更高。
Silvia Esparrachiari, Tanya Reilly, Ashleigh Rentz - 跟踪和控制微服务依赖关系
如果您曾经将钥匙锁在房屋或汽车内,那么您将熟悉依赖循环。没有钥匙您就无法打开锁,但是不打开锁您就无法获得钥匙。有些循环是显而易见的,但是更复杂的依赖循环可能很难在它们导致中断之前找到。跟踪和控制依赖关系的策略对于维护可靠的系统是必要的。
Diptanu Gon Choudhury, Timothy Perrett - 为互联网规模的服务设计集群调度器
希望构建调度系统的工程师应考虑他们使用的底层基础设施的所有故障模式,并考虑调度系统的运营商如何配置补救策略,同时帮助租户系统在租户系统的所有者进行故障排除期间尽可能保持稳定。