Azure云计算

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】

我们最近在第 23 届 ACM操作系统原理研讨会 (SOSP)上发布了一篇描述Windows Azure存储内部详细信息的论文。
您可以在这个链接11-calder.pdf下载到PDF版论文。会议还公布了谈话的视频链接在Youtube,和PPT幻灯片文档11-calder.pptx

这篇论文详细介绍了如何通过存储集群提供和扩展中心内部及跨数据中心的存储能力, 以及如何使用存储位置服务(location service)来管理我们的集群和存储帐户。然后,文章集中介绍了集群的三层体系结构(前端层、分区层和流层),我们为什么会有这三层,这三层的功能是什么、如何工作以及集群内和集群间的两个复制引擎。此外,文章概述了我们所做出的一些设计决策/权衡取舍,以及从构建这一大型分布式系统中获得的经验和教训。

Windows Azure存储的设计旨在为我们预期会看到的体系结构中各种类型的网络分区提供一致性、可用性和分区容错 (CAP)(这三者缺一不可)。我们通过共同设计分区层和流层,为集群内常见的分区/故障(如存储结点和机架层的网络分区)提供强一致性、高可用性和分区容错。

在这个简短的会议讲话中,我们讨论了一些主要详细信息,其中包括分区层如何对每个存储集群提供可扩展至数以千亿级对象的自动负载均衡对象索引;流层如何执行集群内复制和故障处理;以及如何通过共同设计分区层和流层来为存储结点和机架层的网络分区和故障提供一致性、可用性和分区容错。

署名:布莱德·考德(Brad Calder)   标签:架构性能可伸缩性

以上消息来源:https://blogs.msdn.microsoft.com/windowsazurestorage/2011/11/20/sosp-paper-windows-azure-storage-a-highly-available-cloud-storage-service-with-strong-consistency/

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图


原文名称:
《Windows Azure Storage: A Highly Available Cloud Storage Service with Strong Consistency 》

微软Microsoft公司享有著作权,英文PDF原文链接http://sigops.org/sosp/sosp11/current/2011-Cascais/printable/11-calder.pdf

本站展示的论文译文源自Google搜索引擎且未有任何改动,2011 ACM 978-1-4503-0977-6/11/10 版权所有

翻译内容未有任何改动,但由于翻译资料源自2011年的SOSP论文,所以文中资料仅供参考。

**原文警告:允许因个人或课堂需要 , 免费复制本文件全部或部分内容的数字或实体版,前提是不得用于盈利或商业用途,而且复本必须在第一页完全引用这一条的内容。如果以其他方式复制、重新发布、上传至服务器,就需要实现征得许可并 / 或支付费用。SOSP ’11, 葡萄牙卡斯卡伊斯(Cascais)2011年10月23-26日 2011 ACM 978-1-4503-0977-6/11/10 版权所有**

**Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. SOSP ’11, October 23-26, 2011, Cascais, Portugal. Copyright © 2011 ACM 978-1-4503-0977-6/11/10**

全文共计:24129个字数  64223个字符,阅读时间预计需要5小时。


Windows Azure Storage: 融合高可用性和强一致性的云存储服务

Brad Calder, Ju Wang, Aaron Ogus, Niranjan Nilakantan, Arild Skjolsvold, Sam McKelvie, Yikang Xu, Shashwat Srivastav, Jiesheng Wu, Huseyin Simitci, Jaidev Haridas, Chakravarthy Uddaraju, Hemal Khatri, Andrew Edwards, Vaman Bedekar, Shane Mainali,Rafay Abbasi, Arpit Agarwal, Mian Fahim ul Haq, Muhammad Ikram ul Haq, Deepali Bhardwaj, Sowmya Dayanand, Anitha Adusumilli, Marvin McNett, Sriram Sankaran, Kavitha Manivannan, Leonidas Rigas

Microsoft 摘要

Windows Azure Storage(WAS)是一种可在任何时间为客户存储无限量数据的云 存储系统。WAS的客户可以随时随地访问数据,而且只需要为其使用和存储的内容付费。在WAS中,数据同时采取本地和异地复制的方式实现灾备。目前WAS 提供Blob(文件)、Table(结构化存储)和Queue(消息传递)这3种存储形式。 在本文中,我们将对WAS的构架、全域命名空间、数据模型以及其资源配置、 负载均衡和复制系统进行说明。

类别与主题内容

D.4.2 [操作系统]:存储管理— 次 存储 ;
D.4.3 [操作系统]:文件系统管理— 分布 式文件系统 ;
D.4.5 [操作系统]:可靠性— 容错 ;
D.4.7 [操作系统]:组织结构与 设计— 分布式系统 ;
D.4.8 [操作系统]:性能—度量。

通用术语

运算法则、设计、管理、度量、性能、可靠性。

关键词

云存储、分布式存储系统、Windows Azure。

1、概述(Introduction)

Windows Azure Storage(WAS)是一种最早于2008年11月出现的可扩展云存储系统。Microsoft内部在各类应用中使用该系统,如社交网络搜索、视频、音乐和游 戏服务、医疗记录管理等。除此以外,还有许多Microsoft以外的客户也在使用 WAS,而且任何人都可以通过互联网注册使用该系统。

WAS采用Blobs(用户文件)、Tables(结构化存储)和Queues(消息传递)这几种形式提供云存储服务。这三种数据抽象类型可为各类应用提供整体的存储和工作流。我们看到的最常用的模式是,进出的数据通过Blobs移动,Queues提供处理Blobs所需的整体工作流,中间服务状态和最终结果都保存在Tables或Blobs 里。

**允许因个人或课堂需要 , 免费复制本文件全部或部分内容的数字或实体版,前提是不得用于盈利或商业用途,而且复本必须在第一页完全引用这一条的内容。如果以其他方式复制、重新发布、上传至服务器,就需要实现征得许可并 / 或支付费用。**

这种模式的其中一个例子就是,在Windows Azure上创建的摄入引擎服务 (ingestion engine service)能提供接近实时的Facebook和Twitter搜索。有一个 的庞大数据处理管道,这种服务只是一个庞大数据处理管道的一部分,这个管道能在15秒内搜索到Facebook或Twitter用户发帖或状态更新中可搜索的公开内容(通过我们的搜索引擎Bing)。Facebook和Twitter将原始公开内容发送至 WAS(如用户发帖、用户状态变更等),让这些信息能够公开搜索到。这些内容都存储在WAS的Blobs中。摄入引擎诠释这些数据,用的是用户身份验证、 垃圾邮件和成人内容评定;内容分级;语言和命名实体的分类。除此以外,引 擎还会抓取和扩展数据中的链接, 在处理过程中,摄入引擎快速的访问WAS Tables, 并将结果保存回Blobs中。然后这些Blobs会保存在Bing搜索引擎中,让其内容能够公开搜索到。摄入引擎利用Queues来管理工作流、索引流程以及将 结果存入搜索引擎的时间。从本文角度出发,Facebook和Twitter的摄入引擎在 WAS中存有大约350TB的数据(复制前)。在交易性能上,摄入引擎的最高负 载量可达到约40,000件交易/秒,每天的交易处理量在20亿至30亿之间(参见第 7章有关附加工作负载的讨论内容)。

构建WAS的过程中,内外部潜在客户的反馈推动了许多设计决定。从这些反馈中汲取的一些关键设计功能包括:

  • 强一致性——许多客户想要强一致性,尤其是将业务应用转至云的企业客户。他们还希望在强一致性数据上有条件的读写和删除,从而实现最佳并发控制[12]。 因此,WAS提供了CAP原理[2]宣称难以同时实现的三种特性:强一致性、高可用性和分区容错性(参见第8章)。
  • 全域可扩展命名空间/存储——为了便于使用,WAS采用了全域命名空间,可在全世界任何地方以相同的方式存储和访问数据。因为WAS的主要目的是存储海量数据,因此,全域命名空间必须能够覆盖艾字节数量级及以上的数据。我们会 在第2章中对命名空间的设计展开讨论。
  • 灾备——WAS利用多个彼此相聚数百英里的数据中心保存数据。这种冗余模式对数据遇到,如地震、野火、龙卷风、核反应堆熔毁等灾难后的恢复提供了必要的保障。
  • 多租户和存储成本——为了降低存储成本,同一个共享存储基础设施会提供给多 个客户。WAS混合了不同客户、不同资源需求的工作负载,因此,与在自己专用硬件上运行这些服务相比,WAS在单个时间点上对于存储的需求会大大降低。

在下面的章节中我们还会对设计的特点进行具体的分析。本文后续部分的结构安排如下: 第2章描述用于访问WAS Blob、Table和Queue 不同数据类型的全球命名空间。第3章会给出WAS架构及其三个层次的总体概述,这三个层次包括:流层 (Stream)、分区层(Partition)和前端层(Front-End)。第4章介绍流层,第5 章介绍分区层。第6章介绍Windows Azure应用访问Blobs和Tables所经历的吞吐量。第7章对Microsoft内部使用WAS进行说明。第8章探讨设计选项和经验教训。 第9章提出相关内容,第10章是对本文的总结。

2、全域分区命名空间(Global Partitioned Namespace)

我们存储系统的一大目标是提供单一的全域命名空间,让客户可以在云中确定其 存储内容的地址,并且随时根据需要扩充存储量。为了达到这一点,我们将DNS作为存储命名空间的一部分,并将存储命名空间分成3个部分:一个账户名 (AccountName)、一个分区名(PartitionName)和一个对象名(ObjectName)。 因此,所有数据都可以通过以下形式的URI访问:
http(s)://AccountName.<service>1.core.windows.net/PartitionName/ObjectName

AccountName是客户选择的、用来访问存储内容的账户名,是 DNS主机名称的一部分。AccountName DNS转换是用来确定主存储集群和数据存储中心的位置。 主位置就是该账户所有数据请求的目标地点。某种应用可能使用多个 AccountNames 跨过不同的位置来存储其数据。
当存储集群收到请求后,PartitionName和AccountName一起确定数据的位置。 PartitionName可以根据流量要求,在存储点之间移动,以访问数据。

当一个PartitionName持有许多对象,使用ObjectName来确认该分区范围内的具 体对象。系统支持拥有相同PartitionName值的不同对象间的原子操作事务(atomic transactions)。ObjectName是可选的,因为对于某些数据来说,PartitionName就已经能够唯一确定该账户下的对象。 这种命名方式让WAS能够灵活地支持其3种数据抽象2形式。对于Blobs来说,整体blob名称就是PartitionName。对于Tables来说,表格中每一实体(行)都是一个包含两个属性的基础键值:PartitionName 和 ObjectName。这种特点让使用 Tables的应用将不同的行归入同一个分区,并对它们执行原子操作事务。对于 Queues来说,队列名就是PartitionName,而且每条消息都有一个ObjectName来确认其在队列中的唯一性。

3、高层体系构架(High Level Architecture)

现在我们将对WAS构架进行一个高层次的讨论,看一下它是如何与Windows Azure云平台相互融合的。

3.1 Windows Azure云平台

Windows Azure云平台可以在不同的数据中心和不同的地理位置运行不同的云服务。Windows Azure Fabric Controller是资源配备和管理层,为Windows Azure平台上的云服务提供资源配置、部署/升级和管理。WAS就是这种在Fabric Controller 之上运行的服务。
Fabric Controller为WAS系统提供节点管理、网络配置、健康监测、服务实例启用/停止、业务部署等功能。除此以外,WAS还可以通过Fabric Controller检索网 络拓扑结构的信息、集群的物理布局、存储点的硬件配置。WAS负责管理磁盘 间的复制和数据位置及平衡存储集群内的数据和应用流量。

3.2 WAS构架的组成部分

WAS的一大特点就是存储和访问海量(EB甚至更多)存储空间的能力。目前我们正在构建70PB的原始存储空间,2012年根据客户要求我们还会配置几百PB的 原始存储空间。

1 <服务>规定了服务的类型,可以是 Blob、Table 或 Queue。 2 2 Windows Azure Blobs、Table 和 Queues 的 API 参见如下网址:http://msdn.microsoft.com/enus/
WAS生成系统由存储集群(Storage Stamp)和位置服务(Location Service)构成 (见图1)。

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图1
图1:高层构架

  • 存储集群(Storage Stamp)——存储集群是指N个支架的存储节点的集群,这里每个支架都构建成为一个独立的有冗余网络和电源的容错域。集群一般由10至20 个支架组成,每个支架上有18个带磁盘的存储节点。我们的第一代存储集群每个大约有2PB的原始存储空间。我们的下一代存储集群每个大约能够有30PB的原始存储空间。为了提供低成本的云存储,我们需要确保我们的存储配置尽可能的被高效利用。我们的目标是将存储集群在容量、事务处理和带宽方面的利用率保持在70%左右。我们会尽力避免利用率超过80%,因为我们还有以下考虑,需要留出20%: (a)磁盘短行程,利用磁盘的外磁道获取更短的搜索时间和更大的带宽;(b) 在某个集群出现支架故障的情况下继续确保存储容量和可用性。当一个存储集群 的利用率达到70%时,位置服务会通过集群间复制将账户迁移到不同的集群(参 见第3.4节)。
  • 位置服务(Location Service,缩写LS)——位置服务管理所有的存储集群。同 时还负责所有存储集群间账户命名空间的管理。LS将账户分配给各存储集群, 管理它们在存储集群间的灾备和负载均衡。位置服务本身分布在两个不同的位置,从而也起到了灾备的作用。

WAS提供在以下三个地区不同地点的存储服务:北美、欧洲和亚洲。每个地点 都是一个数据中心,在当地有一幢或多幢建筑,并且每个地点拥有多个存储集群。 为了提供额外容量,LS还具备添加新区域、为某个区域添加新地点或为某个位 置添加新存储集群的功能。因此,如果要增加存储容量,我们可以将一个或多个存储集群分配给我们所要求的那个地点的数据中心,然后再将其添加进LS。然后,LS可以将新的存储账户分配至为客户使用的新集群,并且对从旧集群转至新集群的已有存储账号进行负载平衡(转移)。

图1展示了有2个存储集群的位置服务以及存储集群内各层次的情况。LS对不同位置的存储集群使用的资源进行追踪。当应用程序要求创建一个新账户来存储数据时,也规定了存储的地点关系(如北美)。然后LS就依据所有集群的负载信 息来推断,在该地点内选择一个存储集群作为该账号的基础集群,(考虑存储集 群的完整性和其它因素,如网络和事务处理利用率)。接着,LS将账户元数据 信息存储在选定的存储集群中,让存储集群开始接收指定账户的流量。LS更新 DNS,让请求通过路径名https://AccountName.service.core.windows.net/,到达存储集群的虚拟IP(VIP,即存储集群用来接收外部数据流的IP地址)。

3.3 存储集群内的三个层

如图1所示,一个存储集群内有三个层次,从下到上分别为:

  • 流层(Stream Layer)——这一层在磁盘上存储数据,并且负责数据在不同服务器间的分配与复制,从而确保数据在存储集群上的持久性。流层可以想象成集群内的一个分布式文件系统层。它理解被称为的“流”的文件(即大存储块的有序列 表,即“区间”),也理解文件存储和复制的方式等,不过它不了解高级别对象的构成或其语义。数据存储在流层,但是需要从分区层访问。实际上,分区服务器 (分区层的守护进程)和流服务器同时位于存储集群的每个存储节点上。
  • 分区层(Partition Layer)——分区层构建的目的有(a) 管理和了解高级别数据抽象形式(Blob、Table、Queue),);(b)提供可扩充的对象命名空间;(c) 为对象提供事务处理命令和强一致性;(d)在流层之上存储对象数据;(e)缓冲对象数据以减少磁盘的I/O。这一层另一个作用是通过在同一个集群内数据对象的分区配置,实现可扩展性。 如前文所述,所有对象都有同一个PartitionName;根据PartitionName的数值,对象被分解成不相交的多个部分,由不同的分区服务器控制。这一层负责管理分区服务器服务PartitionName对应Blobs、Table、Queue的不同范围。除此以外,它还提供不同分区服务器间PartitionName的自动负载平衡功能,以满足对象的流量需求。
  • 前端层(Front-End layer)——前端(FE)层由一组接收传入请求的无状态服务器构成。收到请求后,FE会查找账户名,验证该请求并授权,然后将请求送至分区层的分区服务器上(根据PartitionName)。系统存有一张分区图(Partition Map),用于追踪PartitionName的范围,以及哪个分区服务器对应哪些 PartitionName。FE服务器会缓存这张分区图,用这张图判断每个请求需要发送至 哪个分区服务器。FE服务器也可以直接从流层接收大型对象,缓存经常被访问的数据以此提高效率。

3.4 双复制引擎

在详细描述流层和分区层之前,我们首先简要概述一下我们系统中使用的两个复制引擎以及它们各自的职责。

  • 集群内复制(Intra-Stamp Replication)(流层)——该系统提供同步复制 ,并重点确保所有写入集群中的数据持久保存在该集群内。它在不同故障域的不同节点保存了足够的数据拷贝,以便在磁盘、节点和支架发生故障时,数据在集群内可以持久保存。集群内复制完全由流层来完成,并且操作处在客户写请求的关键路 径上。一旦某事务已被集群内复制的方式成功复制,该成功消息才发送给客户。
  • 集群间复制(Inter-Stamp Replication)(分区层)——该系统提供异步复制 , 侧重点在于跨集群的数据复制。集群间复制在后台完成,并且不处在客户请求的关键路径上。该种复制是对象级别,要么复制整个对象,要么为某指定账户复制最近的增量变化。集群间复制用于(a)将某账户的数据的拷贝保存在两个地点以用于灾难恢复,以及(b)在集群和集群之间迁移客户的数据。集群间复制由位置服务基于账户配置,并由分区层执行。

集群间复制侧重点在复制对象以及应用于这些对象的事务,而集群内复制则侧重于构成对象的磁盘存储区块的复制。

我们将复制从两个不同层面上的划分为集群内复制和集群间复制,原因如下:集群内复制提供持久性,以应对大型系统中频频发生的硬件故障;集群间复制则提供地理冗余,以应对较罕见的地缘灾难。集群内复制提供低延时很重要,因为其处在用户请求的关键路径上;而集群间复制的重点是在复制延迟达到可接受水平时对集群间网络带宽的最优化利用。它们是两种复制方案解决的不同问题。

创建这两种单独的复制层的另一个原因是其中每个层都须保持命名空间。在流层执行集群内复制,需保存的信息量的范围由一个单独存储集群的大小来决定。这点允许集群内复制完成后的所有状态可以在内存中缓存起来以提高性能(参阅第 4节),使WAS能够通过在一个集群内响应客户请求快速提交事务,从而提供具有强一致性的快速复制。相比之下,与位置服务相结合的分区层控制并理解跨集群的全局对象命名空间,允许其跨数据中心进行有效的复制并保持对象状态。

4、流层(Stream Layer)

流层提供一个仅由分区层使用的内部接口。它提供一个类似于命名空间和API的文件系统,区别仅在于所有的写操作都只作数据添加(append-only)。它允许客户端(分区层)对这些称作流(stream)的大文件进行打开、关闭、删除、重命名、 读、追加和连接操作。一个流是一个区间(extent)指针的有序列表,而一个区间 则是一个追加块的序列。

图2显为流“//foo”,其中包含(指针指向)四个区间(E1、E2、E3和E4)。每个区间包含其所添加的一组区块。E1、E2和E3是封闭的区间。这意味着它们不能再被追加数据;在一个流中仅有最后一个区间(E4)可以被追加数据。如果某个应用从头至尾读取该流的数据,它会按照E1、E2、E3和E4的顺序依次获得这 些区间的区块内容。

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图2

图2:带有四个区间的流的实例

这些数据概念详细阐述如下:

  • 区块(Block)——这是用于数据读写的最小单位。一个区块可以有N个字节(例: 4MB)。数据作为一个或多个串连的区块被写入(添加)一个区间,在该区间中区块不必大小相同。客户端按照区块进行追加并控制每个区块的大小。一次客户端读取给予一个流或区间一个偏移量,而流层可以根据偏移量的需要读取区块来完成读取的长度。当执行一次读取时,一个区块的全部内容都会被读取,这是因为流层将其校验和验证存储为区块级别,每区块一个校验和。整个区块经过读取以校验和验证,而且每次区块读取时都检查一遍。此外,系统中的所有区块都会对照其校验和进行验证,数日一次,以检查是否有数据完整性问题。
  • 区间(Extent)——区间是流层中的复制单位,默认的复制策略是针对一个区间在一个存储集群内保存三个复本。每个区间都由一系列区块组成,且被存储在一个 NTFS文件中。由分区层使用的目标区间大小是1GB。存储小对象时,分区层将其中许多对象添加至相同区间甚至在相同的区块中;存储TB级大对象(BLOB) 时,对象则被分区层分解到许多区间上。作为其索引的一部分内容,分区层持续跟踪对象存储的流、区间,以及区间中的字节偏移量。
  • 流(Stream)——每个流在流层所维护的分层命名空间中都有一个名称,一个流就像是发到分区层的一个大文件。可以给流追加数据,并且可以随意读取。一个流 是一个有序的区间指针(pointers to extents)列表,由流管理器(Stream Manager)负责维护。当区间被串连在一起时,它们代表完整的连续地址空间,在该地址空间内,可以按照它们被添加到流的顺序对流进行读取。通过串连来自现有流的区间, 可以构建一个新的流,这是一种快速操作,因为它只是对指针列表作更新。在流 中只有最后一个区间可以被添加数据,而其他所有前面的区间均恒定不变。

4.1 流管理器和区间节点

流层的两大主要架构对象是流管理器(SM)和区间节点(EN)(如图3所示)。

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图3

图3:流层架构

  • 流管理器(SM)——SM持续记录流的命名空间、每个流中的区间,以及跨区间节 点(EN)的区间分配。SM是一种标准的Paxos集群[13],如同之前的存储系统[3]中 使用的那样,并且不处在客户请求的关键路径上。SM负责(a)维护流命名空间和 所有活动的流及区间的状态,(b)监视EN的状况是否良好,(c)创建并分配区间至 EN,(d)执行因硬件故障或不可用而丢失的区间拷贝的延后复制,(e)回收不再被 任何流所指向的区间,以及(f)根据流策略(参阅第4.4节)调度执行对区间数据 的纠删编码。SM定期测验(同步)EN的状态及其存储的区间。如果SM发现某个区间所复制 到的EN数量少于预期,SM将缓慢地创建该区间的再复制,以重新获得所需的复制水平。对于区间拷贝的位置,SM会跨不同故障域随机选择各个EN,这样就可以将它们存储在不会遭受由电力、网络、或因处在相同支架上而导致关联故障的 节点上。SM不了解关于区块的任何信息,而只是流和区间。SM不处在客户端请求的关键路径上,且不跟踪每个区块的添加,这是因为区块可能总数巨大且SM没有足够 能力对那些数据进行跟踪。由于流和区间的状态只在一个单独的集群内被跟踪, 所以状态的量可以保持足够的小以适配SM的内存。流层的唯一客户端就是分区 层,而且分区层和流层经过联合设计,它们使用的区间不超过50,000,000个,在给定我们当前集群规格下,单个存储集群使用的流不超过100,000。这种参数规 定下,很容易给SM定义配置32GB内存。
  • 区间节点(EN)——每个区间节点维护着由SM分配给其存储的一组区间拷贝。一个EN拥有N个的磁盘,由其完全控制,用于存储相关区块以及区间拷贝。EN不 知道关于流的任何信息,只处理区间和区块。在EN服务器内部,磁盘上的每个区间都是一个存放有数据区块及其校验和的文件,以及一个映射区块的区间偏移量及其文件位置的索引。每个区间节点都包含其所拥有的区间视图以及指定区间的对等拷贝的所处位置。该视图是由SM保存的全域状态中的EN所做的一种缓 存。EN仅与其他EN进行通讯,复制由客户端发出的区块写入(追加),或在SM 的指示下创建一个现有拷贝的更多复本。当一个区间不再被任何流引用时,SM 回收站便收集该区间并通知EN回收其空间。

4.2 添加操作和封闭区间

流只能被追加,已有数据则无法更改。

追加操作是原子操作:要么追加整个数据区块,要么什么也不追加。可以通过单个“多区块追加”的原子操作一次追加多个区块。流读取的最小单位是单个区块。“多区块追加”操作使我们能够一次追加写 入大量序列数据,并在此后进行零星的读取。按照在客户端(分区层)和流层之间使用的约定,多区块追加将以原子级的方式进行,且如果客户端未收到针对请求的反馈(由于故障)则应重新尝试请求(或封闭相应区间)。

该约定暗示,在遇到超时情况时,客户端需要预料到相同的区块会被添加多次,并正确处理重复的记录。分区层以两种方式处理重复的记录(有关分区层流的详情,请参阅第5 节)。对于元数据流和交付日志流,所有写入的事务都有一个序列号,而重复的记录将有相同的序列号。对于行数据流和BLOB数据流,若发生重复写,RangePartition数据结构仅会指向最后一次写入,因此,之前的重复写入将因没有任何引用而被收入回收站。

一个区间具有一个目标尺寸,由客户端(分区层)规定,当填充至该尺寸大小时, 区间会在一个区块边界上被封装,随后一个新的区间会加入到流中,而追加操作会继续对新区间展开。一旦区间被封装,则无法再对其做追加操作。一个封装的区间始终恒定不变,而流层会对封装区间进行一些优化操作,例如用纠删码处理相应区间。在一个流中的区间不必具有相同的大小,它们可以随时被封闭,甚至可以任意增大。

4.3 流层内部集群复制

通过对流层和存储层的共同设计,可实现二者在对象事物级的高度一致。基于以下各项,存储层提供的高度一致性的正确性得以确保:

1、 一旦一条记录被客户端追加或回收,随后读取任何复本的记录,都仅能看到相同的数据(此数据无法更改)。
2、 一旦封装了某个区间,对任何密封复本的读取,总是看到该区间相同的内容。

数据中心、光纤控制器和 WAS 中都设定安全机制以防范恶意的对手,所以流复制中未设定处理相同威胁的机制。我们考虑了从硬盘和节点错误到电源故障、网络问题、位翻转和随机软件错误、软件漏洞的一系列故障,这些故障将会导致资料损坏,而校验正是用来检测此种损坏。在此背景下,本章节的其余部分将会讨论内部集群复制机制。

4.3.1 复制流程

如图 3 所示,在初次创建一个流时(步骤 A), SM 会为第一个范围分配三个复本(一个主用、两个备用),并分配到三个区间节点(步骤 B)。考虑到区间节点的用途(用于负载平衡),这三个节点由 SM 选定,并在故障和升级域之间的随机分布复本。另外,SM 会为区间设定一个主要复本。数据必须由客户端写入到主用 EN,而主用 EN 负责协调两个备用 EN 的写入操作。在追加区间时(未密 封区间),主用 EN 和三个复本的位置不可变动。由于当区间未被密封时主用 EN 是固定的,所以无需特意为该区间标记一个主用 EN。

当 SM 分配了区间,区间信息会被回传至客户端,以告知哪些 EN 储存了复本以及哪个才是主用 EN。此状态会作为流的元数据信息的一部分由 SM 储存并由客户端缓存。当流中最后一个追加的区间变成密封状态后,此步骤会重复进行。届时,SM 会重新分配一个区间作为流中的最后区间,所有的追加也会转到这个新的最后区间上。

对于某个区间来说,每次追加都会为区间的复本复制三次。客户端会将所有的写入要求发送给主用 EN,但是客户端可以从任意复本中读取数据,甚至包括未密封的区间。客户端将追加发送到区间的主用 EN,之后,主用 EN 将负责(a)决 定区间中追加的偏移量;( b)如果相同的区间中存在并发的未完成的追加要求时, 为所有追加排序(选择追加的偏移量);(c)将追加及其所选的偏移量发送到两个备用 EN;( d)当三个 EN 在磁盘上出现成功追加时,仅将成功的追加发送回客户端。追加时的步骤顺序详见图 3(以数字表明顺序)。仅当三个复本的写入 操作全部成功时,主用 EN 才会告知客户端追加成功。如果同一个区间内发生多个未完成的追加,主用 EN 会根据它们偏移量的顺序(设定偏移量顺序)依次告知客户端追加成功。在复本的追加顺序设定完毕时,最后一个追加的位置会作为复本的当前交付长度。由于区间内的主用 EN 未发生变化,主用 EN 总是为追加选择偏移量,区间的追加按顺序交付,我们可以确保所有复本的比特位全部一致, 以及出现故障时,区间是如何被封装(详见在 4.3.2 章节)。

当打开一个流时,区间的元数据在客户端缓存,所以客户端可以不通过 SM 直接 访问 EN 进行读写操作,直到下一个区间需要被分配到这个流中。如果再写入时某个复本的 EN 无法访问或者某个复本存储的磁盘发生了故障,写入失败通知将会回传至客户端。之后,客户端会联系 SM,SM 会将正在追加的区间以其当前确认长度封装(详见 4.3.2 章节)。此时,被封装的区间不可再被追加。SM 之后 会分配一个带有复本的区间到不同的(可用的)EN,并使其成为流中的最后一 个区间。新区间的信息会被回传至客户端。SM 封装区间并分配新区间的平均时长为 20 毫秒。这种机制的好处在于,客户端可以在一个新区间分配之后立刻将其追加至流中,而并不需要等到某个特定的节点重新恢复正常再进行追加。

如果需要,SM 可以在后台中为新封装的区间创建复本,从而使其回到期望中的冗余级别。

4.3.2 封装

从一个更高的层次上来看,SM 协调了 EN 之间的封装操作。SM 可以根据区间复本的交付长度来决定用于封装的区间交付长度。一旦区间封装完毕,交付长度将不会改变。

为完成区间封装,SM 会询问三个 EN 的当前长度。在封装过程中,会出现两种情况:

1.所有复本的长度相同,此种为简单情况;
2.某个范围的复本比其他复本 长或者短。在仅有部分(而非全部)用于复本的 EN 可用而出现追加失败时,情况 2 才会发生(例如:仅有部分复本得到了追加区块,而非全部)。

即使 SM 未能访问所有涉及到的 EN,我们也可以确保 SM 可以封装区间。在区间封装过程中,SM 会从它能通信的可用 EN 中选择确认长度最小的一个。此种选择并不会 导致数据丢失,因为只有在所有三个 EN 的全部复本都写入磁盘时 EN 才会回传 成功通知。这就意味着最短确认长度中一定包含所有客户端的写入信息。另外, 即使最后长度包含的区块没有回传至客户端也无关紧要,因为这些数据已经被客 户端(存储层)正确处理过了,如 4.2 章节所述。在区间封装过程中,SM 会以其选择的密封长度封装所有可访问的区间复本。

一旦封装结束,区间的确认长度不可更改。如果在封装过程中,一个 EN 不可被 SM 访问,而之后又可访问了,那么 SM 会强制 EN 将其给定的区间长度与 SM 选择的交付长度同步。这样就保证了一旦一个区间封装之后,该区间的所有可用复本(SM 最终可以访问的复本)按位相同。

4.3.3 与分区层的相互作用

一个有趣的例子是,由于网络分区,客户端(分区服务器)仍能与在密封过程无 法与SM通信的EN通信。本节对分区层处理这种情况的方式进行了说明。

分区层有两种不同的读取模式

1. 已知位置的读取记录。分区层使用两种类型的数据流(行和blob)。这些数据 流始终在特定的位置读取(范围+偏移量、长度)。更重要的是,分区层只用前一次成功追加返回的位置信息在流层上读取这两种数据流。仅当该追加被成功提交到所有三个复本时,才会发生这种情况。复制方案可确保这种读取 看到的始终是相同的数据。
2. 将所有记录在分区负载的流中依次列举。每个分区都有两个额外的流(元数 据和交付日志)。 这些是分区层从流的起始点到最后一条记录依次读取的流。 仅当加载分区(在第5节中说明)时,才会发生此操作。分区层确保这两种流在分区负载期间不会发生来自分区层的有用追加。然后,分区和流层共同确 保记录以相同顺序返回到分区负载上。

在分区负载开始时,分区服务器会向这两种流最后区间的主 EN 发送一个“检查 确认长度”,可以用来检查所有复本是否可用,以及它们的长度是否相同。如果没有,则说明区间被封装,分区负载期间仅对被 SM 封装的复本进行读取。这样确保分区负载可以看到自己所有数据和完全相同的视图,即使我们在流的最后区间上重复加载不同封装复本的相同分区。

4.4 纠删编码密封区间

为了降低存储成本,WAS纠删方案对 Blob 存储的密封区间进行编码。WAS 在 块边界处将一个区间分成 N 个大致相等大小的片段。然后,它使用 Reed-Solomon 添加 M 个纠错代码片段,用于删除编码算法[19]。只要失去的片段(数据片段+代码片段)数不超过 M 个,WAS 就能重新创建完整的区间。

鉴于我们存储的数据量,对密封区间应用纠删编码是一种重要的优化。根据所使用的片段数量,它将一个集群内三个完整复本的数据存储成本从原始数据的三倍减少到原始数据的 1.3-1.5 倍。此外,与将三个复本保存在一个集群内相比,纠删编码实际上提高了数据的耐久性。

4.5 读取负载均衡

当读取带三个复本的区间时,这些读取带有一个“限期时间”值,该数值规定,如果无法在限期内进行读取,则不应尝试读取。如果 EN 确定无法在期限内完成读取,它会立即回复客户机无法满足该限期。利用该机制,该客户机可以选择另 一个 EN 读取该数据,还可以更快地完成读取。

该方法还能与纠删编码数据一同使用。当某数据片段因磁盘负载过重而无法及时被读取时,可通过重建而不是读取该数据片段来更快速地提供该读取服务。在这种情况下,读取请求(用于满足客户请求所需的片段范围)会被发布到某个纠删编码区间的所有片段,而且前 N 个响应会被用来重建所需的片段。

4.6 硬盘主轴防饥饿(Spindle Anti-Starvation)

很多硬盘驱动器经过优化来实现最大吞吐量,而实现该目标会牺牲公平性。它们往往喜欢连续读取或写入。由于我们的系统中含有很多容量较大的流,我们在开 发服务时观察到,有些磁盘忙于提供大型流水线执行单元的读取或写入,同时会影响对其他操作的服务。我们在一些磁盘上观察到,这会锁定非连续的 IO 长达 2300 毫秒。为避免出现这种问题,当已经调度的预期待定 IO 请求超过 100 毫秒,或已经调度的任何待定 IO 请求但未提供服务超过 200 毫秒时,我们不会给硬盘主轴调度新的 IO。使用我们自定义的 IO 调度,可以实现公平读取/写入, 只需略微增加一些连续请求上的整体延迟时间即可。

4.7 耐久性和日志处理

流层的耐久性约定是,数据被证实由流层写入,则系统中存储的数据至少有三个耐久的复本。该约定可使系统保持数据耐久性,即使遭遇大范围的停电也可以保证。操作我们的存储系统时,在确认所有写入返回至客户端前,须确保它们具有电力安全存储耐久性。

我们需要保持耐久性约定,同时要实现良好性能,其中一部分是对流层进行重要的优化,我们在每个区间节点上保留一个完整的磁盘驱动器或 SSD作为一个日志驱动器,用于该区间节点的全部写入。该日志驱动器[11]专用于写入单个顺序 的数据日志,这使我们可以达到设备潜在的完整写入的吞吐量。当分区层追加流时,数据由主 EN 写入,同时平行发送到要写入的两个次 EN 中。当各 EN 实 施追加时,会 (a) 写入所有数据以追加到日志驱动器,并且 (b) 排队等待写入 EN 数据磁盘的追加操作。二者中只要有一个成功,即可成功返回。如果日志首先成功,数据在前往数据磁盘的同时还要在内存中缓冲,此时内存会提供数据的读取,直到该数据存到数据磁盘上。从这时起,数据磁盘上提供数据的读取。这种方法可以合并对数据磁盘的相近写入而形成较大写入,同时更好地调度并行写 入和读取操作以获得最佳吞吐量。这是以在关键路径上增加写入操作成本,从而获得良好延迟的折衷方法。

虽然流层是一个只追加系统,我们发现添加日志驱动器会带来重要的好处,即追加不需要干涉客户端对数据磁盘的读取操作。日志可使分区层的追加时间有更一致和更低的延迟。以分区层的交付日志流为例,追加的速度与最慢的用于追加复本的 EN 一样快。对于无日志处理的针对交付日志流的小型追加,平均端到端的流追加延迟为 30 毫秒。进行日志处理时,我们看到的平均追加延迟仅为 6 毫秒。此外,延迟的波动水平显著下降。

5. 分区层(Partition Layer)

分区层存储了不同类型的对象,并理解一个事务对给定的对象类型(Blob、Table, or Queue)意味着什么。分区层可提供 (a) 不同类型存储对象的数据模型,(b) 处 理不同类型对象的逻辑和语义,(c) 对象的大规模可扩展命名空间,(d)在多个可用分区服务器间访问对象的负载均衡,(e) 事务顺序和访问对象的强一致性。

5.1 分区层数据模型

分区层提供了一种重要的内部数据结构,称为对象表(OT)。对象表容量巨大,可扩大到 PB 级别。对象表被以动态方式分成 RangePartition (根据表的流量负载)并分布在一个集群中的多个分区服务器(第 5.2 节)上。RangePartition 是 对象表中从一个既定低键值到一个高键值的连续行的范围。一个给定 OT 的所 有 RangePartitions 是不重叠的,并且每行都在某个 RangePartition 中表示。

以下是分区层使用的对象表。账户表存储了分配给集群的各存储账户的元数据和 配置。Blob 表存储了集群中所有账户的所有 blob 对象。实体表存储了集群中所有账户的所有实体行,它用于公开的 Windows Azure 表数据抽象。信息表存储了集群中所有账户队列的所有信息。模式表跟踪所有 OT 的表结构。分区映射表跟踪了所有对象表的当前 RangePartitions 和正在服务各 RangePartition 的分区服务器。该表被前端服务器用于将请求发送到相应的分区服务器。

上述OT 都有一个存储在模式表中的固定结构。Blob 表、实体表和信息表的基础键值含有三种属性:AccountName、PartitionName 和 ObjectName。这些属性 为这些对象表提供了索引和排序顺序。

5.1.1 支持的数据类型和操作

支持 OT 模式的属性类型是标准的简单类型(bool、binary、string、DateTime、 double、GUID、int32、int64)。此外,系统还支持两种特殊类型——DictionaryTypeBlobType

  • DictionaryType 允许在任何时间增加行的灵活属性(即无固定结构)。这些灵活属性以元组的形式(名称、类型、数值)被存储在 dictionary type 内部。从数据访问的角度来看,这些灵活属性具有行的一阶属性,并且像行中的其它属性一样可查询。BlobType 是用于存储大量数据的特殊属性,目前仅由 Blob 表使用。
  • BlobType 避免在“行数据流”中存储具有行属性的 blob 数据位。 相反,可将blob 数据位存储在单独的“blob 数据流”中,并且还可将blob 数据 位的一个指示符(一列“范围+偏移、长度”指示符)存储在行中的 BlobType 属 性中。这使大型数据位与OT的可查询的行属性值分开,这些属性值是存储在行数据流中的。

OT 支持标准操作,包括行上的插入、更新和删除操作以及查询/获取操作。此外, OTs 还可对具有相同 PartitionName 值的不同行进行批事务处理。一个批次中的操作作为一个事务提交。最后,OTs 还能提供快照隔离,以实现读取操作和写入操作同时进行。

5.2 分区层结构

如图 4 所示,分区层有三个主要结构组成部分:一个分区管理器 (PM)、若干 分区服务器 (PS) 和一个锁定服务。

  • 分区管理器 (PM)——负责跟踪并将大量对象表分拆为 RangePartition,并将各 RangePartition 分配给一个分区服务器,提供对象访问。在各集群中 PM 其将对 象表分拆为N 个 RangePartition,跟踪各 OT 目前的 RangePartition 分解,以及 其所分配的分区服务器。PM 将该分配存储在分区映射表中。PM 确保各 RangePartition 在 任 何 时 候 分 配 给 一 个 有 效 的 分 区 服 务 器 , 而 且 两 个 RangePartition 不重叠。它还负责对分区服务器之间的 RangePartitions 进行负载 均衡。每个集群都有多个 PM 运行实例,而且它们会争夺存储在锁定服务中的 首项锁定(见下文)。带首项锁定租约的 PM 是控制分区层的有效 PM。
  • 分区服务器 (PS)——分区服务器服务于 PM 分配的一组 RangePartition 之上的服务请求。PS 将所有分区状态存储到流中,并保持相应的内存缓存以保证服 务效率。该系统保证,通过使用锁定服务的租约,针对同一个 RangePartition 不 会同时存在两个分区服务器提供服务。这可以使 PS 保持对RangePartition服务 对象的强一致性和并发事务的顺序性。PS可同时服务多个来自不同 OT 的 RangePartition。在我们的部署中,PS可以平均服务十个 RangePartition。
  • 锁定服务——用于首要 PM 选择的一种 Paxos 锁定服务[3,13]。此外,每个 PS 还保持一个锁定服务的租约,用于服务分区。由于所使用的概念和 Chubby Lock [3] 页面上说明的概念类似,因此我们不对首要 PM 选择过程或 PS 租约管理 进行详述。

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图4

图 4:分区层结构

在分区服务器出现故障时,故障 PS 服务的所有 N 个 RangePartition 会被 PM 分配到可用的 PS。PM 将根据这些服务器上的负载,选择 N 个(或更少)分 区服务器。PM 将给一个 PS 分配一个 RangePartition,然后更新分区映射表, 指定服务各 RangePartition 的分区服务器。这可使前端层通过在Partition Map Table(见图 4)中搜索,找到 RangePartition 的位置。当 PS 获得一个新任务时,只要 PS 持有其分区服务器租约,它将开始服务新的 RangePartition。

5.3 RangePartition 数据结构

PS 通过维持一组内存储数据结构和一组流中的持续的数据结构,为 RangePartition 提供服务。

5.3.1 持续的数据结构

RangePartition 使用一个日志结构合并树(Log-Structured Merge-Tree )[3]维持 其持续数据。每个对象表的 RangePartition 包括其本身在流层上的一组流,而这组流只属于一个给定 RangePartition 的流,不过由于 RangePartition 分拆,可通过不同 RangePartition 中的多个流指向基础范围 。 以下是组成每个 RangePartition 的流组(如图 5 中所示)。

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图5

图 5:RangePartition 数据结构

  • 元数据流——元数据流是 RangePartition 的根流。PM 通过提供 RangePartition 的元数据流,为一个 PS 分配一个分区。元数据流包含使 PS 载入一个 RangePartition 所需的足够信息,包括该 RangePartition 的交付日志流和数据流名称,以及指向这些流的指示符(范围+偏移),用于指向这些流中开始操作的地方(如从哪里开始处理交付日志流和以及该行数据流的根索引)。服务 RangePartition 的 PS 也可将由RangePartition相关的分解和合并操作状态写入 元数据流之中。
  • 交付日志流——指的是用于存储,自上一个检查点之后,最近的RangePartition 上插入、更新和删除操作的交付日志。
  • 行数据流——存储 RangePartition 的检查点行数据和索引。
  • Blob 数据流——仅用于 Blob Table存储 blob 数据位。

上述各流在流层均为单独的流,由Object Table的 RangePartition持有。

每个Object Table里的RangePartition都只有一个数据流,Blob Table 例外。Blob Table里的RangePartition 有一个用来存储其行检查点数据(blob index)的“行数据流”,以及一个前面描述到的特殊BlobType的 “blob 数据流”,用于存储blob数据。

5.3.2 内存储数据结构

如图 5 所示,分区服务器维持以下内存储组件:

  • 内存表——这是RangePartition 交付日志的内存储版本,含有所有尚未被记录入 检查点的行数据流的最近更新。进行查找时,会对内存表进行检查以寻找 RangePartition 的最近更新。
  • 索引缓存——该缓存存储了行数据流的检查点索引。我们将该索引从行数据索引中分离出来,确保我们为一个给定 RangePartition 保持尽可能多的缓存主索引。
  • 行数据缓存——这是检查点行数据页的内存缓存。该行数据缓存为只读。进行查找时,对行数据缓存和内存表进行检查,优先考虑内存表。
  • Bloom 过滤器——如果未在内存表或行数据缓存中发现该数据,则需要对数据 流中的索引/检查点进行搜索。盲目检查所有索引/检查点的成本非常高。因此,为每个检查点配备一个 bloom 过滤器,显示正在访问的行是否可能在该检查点中。

由于这些组件和[17,4]中的类似,因此我们不做详述。

5.4 数据流

当 PS 收到发送至 RangePartition 的写入请求(如插入、更新、删除)时,它将该操作追加到交付日志中,并将新更改的行放入内存表中。因此,对分区的所有修改都在交付日志中进行持续记录,并反映在内存表中。此时可返回客户端 (FE 服务器)成功标志以完成事务处理。当内存表的大小达到其阈值,或交付日志流的大小达到其阈值时,分区服务器将把内存表的内容写入到对应 RangePartition 的行数据流中的检查点中。然后可将交付日志的相应部分删除。 为了控制 RangePartition 检查点的总数,分区服务器将定期把多个检查点合并为较大的检查点,然后通过垃圾回收机制删除旧的检查点。

对于 Blob 表的 RangePartition,我们还把 Blob 数据直接存储到交付日志流中 (最大限度地减少用于 Blob 操作的流写入数量),但这些数据并不是行数据的一部分,因此不把它们放入内存表中。相反,该行的 BlobType 属性跟踪 Blob 数据位的位置(范围+偏移、长度)。在检查点中,将从交付日志中删除的区间会连接至 RangePartition 的 Blob 数据流。区间连接是流层提供的一种快速操作,因为它只将指向区间的指示符添加到 Blob 数据流末端,并不复制任何数据。

一个 PS 可通过“载入”该分区,开始服务一个 RangePartition。载入一个分区要读取 RangePartition 的元数据流,以定位有效的一组检查点,并重启交付日志中的事务,从而重建内存储状态。完成这些后,PS 就拥有了最新的 RangePartition 视图,并且可以开始服务客户请求。

5.5 RangePartition负载平衡

分区层的一个关键部分就是将这些大量的Object Tables分成RangePartition,并在分区服务器范围内自动对其进行负载平衡,以满足不同的流量需求。

PM进行三种操作在分区服务器范围内分配负载,并控制某个集群里的分区总数:

  • 负载平衡——本操作可识别某个PS流量过多的情况,并将一个或多个 RangePartition重新分配给负载较小的分区服务器。
  • 分割——本操作可对单个RangePartition负载过大的情况进行识别,并将该 RangePartition分成两个或两个以上较小的不交分区,然后在两个或两个以上分区服务器范围内对这些分区进行负载平衡(重新分配)。
  • 合并——本操作将在OT范围内连续键值范围的冷负载或轻负载的RangePartition 合并在一起。合并的作用在于使RangePartition数量与该集群中的分区服务器数量成比例。

WAS保持分区总数介于低水位和高水位之间(一般是一个集群内分区服务器数 量的10倍左右)。 均衡情况下,分区数量将保持在低水位左右的位置。如果预料之外的流量突发集中在单个RangePartition上,则这个RangePartition将被分割来分担负载。当RangePartition的总数接近高水位时,系统将提高合并率并最终使 RangePartition的数量朝低水位的位置靠近。因此,各个OT的RangePartition数量会根据表中对象上的负载发生动态变化的。

基于SM能允许流和区间元数据增长的大小,我们可以选择高水位的 RangePartitions, 其数量可以是一个分区服务器(一个存储集群有几百个服务器) 数量的10倍,这仍与SM内存中的元数据完全匹配。保持RangePartition的数量远高于分区服务器,能让我们快速地将一个失效的PS或支架上的负载分布到其他的PS上。根据集群中RangePartition的当前负载量,给定的分区服务器能结束一个极忙碌的RangePartition、几十个轻载RangePartition,或他们的混搭。Blob Table、 与Entity Table和Message Table的RangePartition数量取决于这些表里对象的负载, 并根据流量在存储集群内不断发生变化。

对于各个集群,通常我们每天会观察到75个分割与合并、200个RangePartition负载平衡。

5.5.1 负载平衡操作明细

我们对各个RangePartition的负载进行跟踪,也跟踪各个PS的总负载。为做到这 两点,我们可以跟踪(a)事务处理量/秒;(b)平均等待事务数量;(c)压制率;(d)CPU使用率;(e)网络使用率;(f)请求等待时间,以及(g)RangePartition的数据量。PM维护着各个PS的心跳信息。这一信息被传回PM并予以处理。根据标准,如果PM认为某个RangePartition负载过大,则会决定分割该分区,它会向 PS发出一个命令来执行分割。相反,如果某个PS的负载过大,但没有单个的 RangePartition有很轻的负载,PM则会从PS中减去一个或多个RangePartition,并给其重新分配负载更轻的PS。

为了实现RangePartition的负载平衡,PM向PS发出一个卸载命令,在卸载之前, PS将会让RangePartition写下当前的检查点。一旦完成,PS会向PM确认卸载已完成,然后PM将这个RangePartition分配给新的PS,并把分区图表更新给新的PS。 新的PS加载后,开始为RangePartition提供流量。由于在卸载之前写下了检查点, 交付日志很小,因此新PS上RangePartition加载速度会非常快。

5.5.2 分割操作

由于RangePartition负载过多,或者它的行数据流或blob数据流大小原因, WAS 会对RangePartition予以分割。如果PM识别了其中任何一种情形,则会告知为 RangePartition服务的PS根据负载或数据流大小进行分割操作。PM决定是否进行分割操作,而PS则选择分区分割的键值 (AccountName, PartitionName)。若根据 数据流大小进行分割,RangePartition会维持分区里对象的总尺寸以及分区大约对半大小的分割键值,PS利用这些来选取在哪进行分割的键值。如果根据负载进行分割,则PS根据自适应范围分析[16]来选择键值。PS自适应地跟踪 RangePartition内哪些键值范围负载最大,并由此确定用什么键值分割 RangePartition。

要将RangePartition(B)分割成两个新的RangePartition(C、D),可以采取以下步骤:

1. PM指示PS将B分割成C和D。
2. 负责B的PS检查B,然后在以下步骤3期间暂时停止提供流量。
3. PS利用特殊的流操作“MultiModify”获得B的各个流(元数据、交付日志和数 据),并分别创建C和D的新流集,新流集的区间和B的一样,顺序也一样。此 步骤十分迅速,因为流就是指向区间的指针清单。然后PS将C和D的新分区键 值范围追加到它们的元数据流中。
4. PS开始向两个新分区C和D的各自不相交PartitionName范围发出请求。
5. PS将分割完成告知PM,PM则更新相应地分区图表及其元数据信息,然后PM 将其中一个分割分区移至不同的PS。

5.5.3 合并操作

为了将两个RangePartition合并,PM将选择流量低的PartitionName范围相邻的两个RangePartition, C和D。采取以下步骤将C和D合并入新的RangePartition E。

1. PM移动C和D,以便由同一个PS提供服务。然后PM告诉PS将(C和D)合并 入E。
2. PS对C和D进行检查,然后在步骤3期间暂停C和D的流量。
3. PS使用MultiModify流命令为E创建新的交付日志和数据流。各个流均为C和D 中各自流中所有区间的连接。这个合并意味着,用于E的新的交付日志流里 的区间将会是C的所有区间,顺序跟在C的交付日志流里一样,之后是D所有 的区间,顺序和原来也一样。这一布局对于新的行数据和Blob数据是相同的。
4. PS构建E的元数据流,元数据流包含了新的交付日志和数据流的名称、E的组 合键值范围、E的交付日志区域的起始位置的指示符(区间+偏移量),其中E 的交付日志源于C 和D, 以及E的数据流中的数据根索引。
5. 此时,可以正确加载E的新的元数据流,PS开始为新合并的RangePartition E 提供服务。
6. 然后PM将更新分区映射表(Partition Map Table)及其元数据信息,以显示该 合并。

5.6 分区层集群间的复制

到目前为止,我们已经讨论了与单一位置和储存集群有关的AccountName(通过 DNS),其中所有数据存取均转到该集群上。我们称这为账户的主集群。实际上,一个账户含有由位置服务分配的一个或多个次集群,这个主/次集群信息会告诉 WAS对这个账号进行从主集群到次集群的集群间复制操作。

集群间复制的一个主要场景,就是在两个数据中心之间进行账户数据的异地复制,以便灾备。在这种情形下,我们需要为账户选择主次位置。例如,对于某个账户,我们想以美国南部位置为主集群(P),美国北部位置为次集群(S)。在 提供账户时,LS会在各个位置选择一个集群,并注册两个集群的账户名,这样美国南部的集群(P)获得即时流量,而美国北部的集群(S)只能从账户集群P 中获得集群间复制(也称异地复制)流量。LS对DNS进行更新,让主机名称 AccountName.service.core.windows.net指向美国南部的存储集群P的VIP。向账户的集群P进行写入操作时,所做的变更会在流层利用集群内部复制的方法在集群 P内被完全复制下来,然后获得的成功信息将返回客户端。在集群P中进行更新 后,集群P中的分区层将使用集群间复制方法,异步将变更异地复制到次集群S 中。集群S变更时,在分区层运用事务处理,在集群S范围内使用集群内部复制 将这一更新全部复制。

由于集群内间复制是异步实现的,因此在出现灾难的情况下,如果最近的更新没 有进行集群间复制,那么数据会丢失。在生产环境中,变更为异地复制,一般在主集群上进行复制后的平均30秒之内在次集群上进行提交。

集群间复制用于账户的异地复制和跨集群的迁移。对于灾备,我们可能需要对可 能丢失的最新变更进行意外的故障转移;但对于迁移,我们进行了整套的失效备援,因此并无数据丢失。在两种故障转移情形下,Location Service将账户的活跃次集群变成新的主集群,并切换DNS指向次集群的VIP。需要注意的是,在故障 转移过后,用于访问对象的URI不发生改变。这使得故障转移后现有的用于访问 Blobs、Tables 和Queues的URI能够继续工作。

6. 应用吞吐量(Application Throughput)

对于我们的云供应,客户可以在VM上作为租户(服务)运行其应用程序。对于 我们的平台,我们在数据中心内部将计算和存储分成各自的集群(群),因为这 一分离可以使得各个集群进行独立的度量,并控制他们的负载平衡。这里我们在 与存储账户数据相同的数据中心里对在VM上的托管服务中运行的客户应用程 序的性能进行了检查。使用的各个VM均为超大VM,完全控制着整个计算节点 和一个速率为1Gbps的网卡。我们收集了基于内外客户共享的生产集群上的结果。

图6显示WAS Table操作的吞吐量:我们基于单个100GB的Table,执行随机1KB 的单一实体上取与存请求,x轴为1-16VMs, y轴为每秒的实体数。这同时也显示 了一次性插入100个实体的批量插入——一种常见的将成群的实体插入到一个 WAS Table中的应用。图7显示,随机取与存4MB blobs 时每秒(y轴)的吞吐量, 与使用VM(x轴)的数量的对比。所有这些结果均为单一储存账户。

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图6

图6:1-16 VMs的表实体吞吐量

微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图7

图7:1-16 VMs的Blob吞吐量

这些结果表明,每秒的实体数在规模上有一个线性的增长,因为此项应用增加了用于访问WAS Table的计算资源量。对于Blobs来说,吞吐量的规模直线上升到 8VM,但在总吞吐量达到测试客户端的网络容量时逐渐减少。结果表明,对于 Table操作来说,批量设置的吞吐量大约是单个设置吞吐量的3倍。这是因为批量 操作使得网络循环的次数明显下降,所要求的流写入较少。此外,Table的读操作比写操作的吞吐量略低。这一差异主要是由于实验的特殊访问模式引起的,实验时在一个大数据集上随机访问了大的键值范围,降低了缓存的效果。另一方面, 写操作通常会引起日志的持续写入。

7. 工作负载概要(Workload Profiles)

基于云的应用程序的使用模式差别很大。第1节已经说明了为Bing提供Facebook 和Twitter搜索的接近实时的摄入引擎。本节我们将描述一些使用WAS的其他内部服务,并针对它们的使用给出一些高层指标。

XBox GameSaves服务已于今年第三季度推出,2011年秋将推出新的功能,将保存的游戏数据放置云中,为上百万XBox用户服务。这项功能将使得订阅的用户能够将游戏进度上传到WAS云存储服务器中,然后用户可通过登陆的任意XBox 控制台进行访问。这一功能的支持存储使用了Blob 和Table储存。

XBox Telemetry服务存储着控制台产生的诊断功能和遥测信息,用于后期安全检 索和离线处理。例如,运行在Xbox 360上的各种Kinect相关功能会产生详细的使用文件,这些文件被传到云上,经客户同意后用于分析和改善Kinect体验。这些上传数据直接存储在Blob中,Table用来维护这些文件相关的元数据信息。Queue 用于协调Blob的处理和清理。

微软的Zune后端使用Windows Azure来储存和传输媒体文件,文件以Blob的形式 存储。

表1展示了,使用WAS的所有服务(内外)时,Blob、 Table和Queue存储中的相关分解以及上述服务。该表展示了Blob、 Table和Queue的请求、容量利用,以及入口与出口流量的分解。

注意:所有服务请求的百分比显示,当所有服务都使用WAS后,大约17.9%的请求为Blob请求,46.88%的为Table为表操作,35.22%的为Queue请求。但就容量来说,70.31%的容量是在Blob中,29.68%的容量被Table使用, 0.01%的容量被Queue 使用。“%Ingress”为Blob、 Table和Queue之间进入流量(字节)的百分比分解; “%Egress”为它们之间的出口流量(字节)的百分比分解。结果显示不同的客户具有不同的使用模式。就容量使用来说,某些客户(例如Zune和Xbox GameSaves) 主要有非结构化数据(例如媒体文件),并将这些数据输入Blob中,而其他一些需要将大量数据编入索引的客户,如Bing和Xbox等,则在Table中拥有大量的结构化数据。与Blob和Table相比,Queue所使用的空间非常少,因为它们多数被用于通讯机制而不是长时间存储数据。

全部    —    请求%    容量%    入口%    出口%
微软Windows Azure Storage云存储:融合高可用性和强一致性的云存储服务 中文版【翻译自SOSP-2011年论文,仅供学习讨论用】插图8
表1:( Blob、 Table和Queue)的使用对比

8. 设计选择与学习到的经验(Design Choices and Lessons Learned)

这里我们要讨论几个WAS设计选择,并和到目前为止学到的一些经验联系起来。

  • 与存储分立的定标计算——早期的时候,我们决定将基于客户VM的计算与微软云操作系统的储存相分离。因此,运行客户服务代码的节点与提供储存的节点相分离。所以我们能够在给定的数据中心中独立地定出计算核心和储存量,以满足客户的需求。考虑到多组织用户管理用途,这一分离还在计算和储存之间提供了一层隔离,使得两个系统独立地实现负载平衡。鉴于这一决定,我们一开始的目标就是让计算以高带宽有效地访问存储器,同一 节点上,甚至同一支架上的数据均不相同。为了实现该目标,我们正在转向下一 代数据中心网络结构[1],这一网络结构使得数据中心网络拓扑变得扁平,并且在计算和存储之间提供了平分带宽。
  • 范围分区与散列法——我们决定使用基于范围的分区层对表的分区/索引,而不是基于散列的索引(根据键的散列值向服务器分配对象)。这一决定的其中一个原因就是基于范围的分区使得性能隔离更加容易,因为给定的账户对象都是一起存储在一系列的RangePartition中的(同时提供有效的对象枚举)。基于散列的方 案能够在服务器范围内简单地分配负载,但没有了隔离和有效枚举的对象局限性。范围分区允许WAS将客户的目标一起保存在他们自己的分区集里,进而控制和隔离可能滥用的账户。

为此,我们采用了基于范围的方案,并建立了自动负载平衡系统(第5.5节),进而根据用户流量通过分割和移动服务器之间的分区来动态分布负载。

范围分区的不足是向外扩展顺序访问模式。例如,如果一位客户将全部数据写入表的键值范围(例如插入键值2011-06-30:12:00:00,然后键值2011-06-30:12:00:02, 然后键值2011-06:30-12:00:10)的尾端,则所有的写操作都会进入客户的表中的最后一个RangePartition。该模式没有利用我们系统提供的分区和负载平衡。相反,如果客户在大量的PartitionName间分配写入,则系统能够快速将表分割成多个RangePartition,并在不同的服务器范围内分担这些分区,使得性能与负载成线形比例(如图6所示)。为了解决RangePartition的顺序访问模式的问题,客户可以使 用分区名的散列或成组,这就避免了以上顺序访问模式的问题。

  • 限制/隔离——服务器时常因客户请求出现超载现象。这就涉及一个难题:服务 器过载时,要限制哪些储存账户,并保证表现良好的账户不受影响。

所有分区服务器记录着每个AccountNames和PartitionNames的请求速率,但是由 于AccountNames和PartitionNames太多,而不能将之全部记录。本系统使用 Sample-Hold计算来跟踪记录前N个最忙的AccountName和PartitionName的请求 速率历史,并据此判定某个账户是否存在违规行为(比如,限制账户后,流量是 否降低)。如果某个服务器出现负载,则以此对进入流量进行选择性限制,进而 找出问题账户。例如:分区服务器依据账户的请求速率历史计算对该账户进入请 求进行限制的概率(账户的请求速率越高,限制的可能性越大,小流量账户则无 须限制)。另外,基于帐户名和分区名的请求历史,本系统可对某个账户的行为 进行判断。负载平衡使得服务器的负载控制在可接受的范围内,但是当访问模式 负载失衡时,(比如:过多流量流向单个PartitionName,高顺序访问流量和重复 的顺序扫描,等等),如果请求速率过高,本系统将限制对于该流量模式的请求。

  • 自动负载平衡——高效的自动负载平衡因可以使分区快速适应各种流量环境,这 点至关重要。正是由于负载平衡,WAS 才能在多租户环境下保持高的可用性, 才能处理单一用户储存账户流量高峰的情况。为了研发一个适用于多租户环境的 系统,我们花费了大量精力收集自适应文件配置信息,发掘哪些指标在何种流量 环境下是最有用的,优化算法以处理我们在生产环境中所见的不同流量模式。

我们从一个用单个数字来量化在每个RangePartition和每个服务器上的负载的系 统开始。首先我们尝试用请求延时和请求速率的结果来表达在分区服务器和每个 RangePartition的负载,这个结果不但很容易计算,而且能反映出对服务器和分区 请求所产生的负载。虽然该方法能很好地满足大多数负载平衡需求(移动附近的 分区),但是它并不能够准确地告知扫描时高CPU利用率或者高网络利用率。因 此,在开发负载平衡技术时,我们要将请求,CPU和网络负载考虑在内。但是这 些指标也不足以准确指导分割决定。

为了引发分区分割,我们引进了不同的机制。在此,我们可以根据一些指标收集 线索找出一些分区是否达极限。比如:我们可以依据请求限制、请求超时、分区 大小等等来引发分区分割。通过结合分割触发因素和负载平衡,系统可以快速实 现在不同服务器间的分区分割和负载平衡。

从一个更高的层次来讲,本算法工作方式如下:每隔N秒(一般为15秒),分区管理器依据每个分割触发因素对所有的RangePartition进行分类。然后PM遍历每 个分区,查看详细的统计数据以决定是否有必要根据上述指标进行分割(负载、 限制、延时、CPU使用情况、大小、等等)。在此过程中,分区管理器选择少量分区进行分割,并在这些分区采取分割动作。

完成分割后,分区管理器依据每个负载平衡指标、请求负载、CPU 负载和网络 负载对所有的分区服务器进行排序,并据此识别哪些是分区服务器是重载和哪些是轻载。接下来,本分区管理器选择那些重载分区服务器,如果在前一次的分割中,产生最近分割,PM 将卸载其中一个 RangePartition 成为一轻载服务器,如果仍然存在高负载分区服务器(没有通过分割卸载),该分区管理器从其中将 RangePartition 卸载成为轻载服务器。

通过配置更新,核心的负载平衡算法可以动态地“交换出去”。WAS包括脚本语言支持,实现自定义负载平衡逻辑,比如定义分区分割是如何基于不同的系统指标触发的。同时由于该脚本语言支持,我们能够根据观察到的各种流量模式对运行 的负载平衡算法进行微调和尝试新算法。

每个RangePartition对应的独立日志文件——存储账户的性能隔离在多租户环境 中至关重要。这也是为什么每个RangePartition使用单独的日志流,为什么 BigTable [4]在同一服务器的不同分区使用单一日志文件。通过单独日志文件,我 们可以隔离每个RangePartition的加载时间以实现在该RangePartition对象的最近 更新。

  • 日志——在原来发布的WAS中没有日志这一功能,结果,在同一驱动器下读写 功能相互角逐,而出现“打嗝”现象,使系统性能大受影响。由于网络流量增加, 我们不希望像BigTable [4]一样写到两个日志文件上(六个复本),尤其因为每个 RangePartition都有单独的日志文件,我们也希望能找到一种优化小型写操作的方法。日志方法便应运而生,该方法使得一个RangePartition配有单一日志文件。该 优化方法不但有效降低了时间延迟,而且实现性能统一。
  • 只追加型系统——只追加型系统和失败时的区间密封大大简化了复制协议和失 败场景处理。在此模型下,数据备份后永远不会被覆盖,一旦备份失败,该区间立即密封。通过所有复本的交付长度,此模型使所有复本保持一致性。

此外,该只追加型系统使得我们能够在基本无成本消耗的情况下对以前的状态进行快照,进而使快照/版本控制特性提供变得更加容易。此系统为我们提供了一些高效的优化程序,比如纠删码。另外,在出错时的问题诊断,系统修复和恢复 方面,只追加型系统有极大益处。保存变更历史使多种工具均可用于问题诊断和系统修复或者恢复,从而使系统从崩溃状态回到和先前已知的一致状态。当在这个层次运行本系统时,使用只追加型系统进行问题诊断和系统恢复给我们带来的利益无以言表。

一个基于附加的系统总有一定成本。一个高效可扩展的垃圾回收系统在保持较低的空间开销方面至关重要,垃圾回收要以额外的输入/输出为代价。另外,由于在硬盘上的数据布局可能异于储存的数据抽象化的虚拟地址空间,所以我们不得不为将大数据集传送给客户实现预取逻辑。

  • 端对端校验和——我们发现对用户数据进行端对端校验至关重要。比如:在大对象上传时,一旦前端服务器接收到用户数据,它就会立即计算其校验和,并将之 随数据发送至后端服务器。然后,每层分区服务器和流服务器在执行下步处理前 核实校验和。一旦发现失配,此次请求将会失败,进而阻止损坏的数据进入系统。 我们发现一些案例中,当一些服务器出现硬件问题,我们的端对端校验和捕捉到了这样的问题,从而保障了数据的完整性。此外,该端对端校验和机制有助于检查出一直存在硬件问题的服务器,我们可以将这些服务器退出业务,作出标志, 并进行检修。
  • 更新——存储集群里的一个存储架为一个容错区域,而概念上与之相对的则是更新区域(系指在一次滚动更新里同时离线一小段时间的一组服务器)。三层中每 一层的服务器都均匀分布在不同的容错区域和更新区域内,提供存储服务。通过上述方式,如果某个容错区域出现故障,我们最多损失所在层里X分之一的服务器,其中X为容错区域的数量。同样,在一次服务更新过程中,特定时间内一层 最多有Y分之一的服务器进行更新,其中Y为更新区域的数量。为了实现这个目标,我们采取滚动更新方式,即一次只更新一个更新区域,这样我们可以在更新存储服务的同时维持较高的可用性。假设有十个更新区域,每次只更新一个区域, 那么理论上讲一次最多可以更新某层服务器的百分之十。

在一次服务更新过程中,存储节点可能会离线几分钟,然后重新上线。由于我们 需要维持一定的可用性,所以必须保证任意时刻都有足够多的有效复本。虽然系 统能够容许一定的离散故障,但我们不至于把这些计划的(且大规模的)更新“故障”当成突发的大规模故障来处理,完全可以采用更加高效的方式来处理。因为更新进程是自动进行的,所以管理数量多规模大的配置不难实现。自动更新进程每次只对某存储集群里的一个更新区域进行更新。某个更新区域离线之前,更新 进程会通知PM移除该更新区域内的分区,并且通知SM不要在该更新区域分配新的扩展。另外,任何服务器离线之前,更新进程会核对SM以确认该更新区域之外的每一个扩展都有足够多的扩展复本。某区域的更新完成后,再更新下一个区域,随后执行一套校验测试,确认系统健康与否。校验可以发现更新进程中出现的问题并且在出现问题后及时阻止,可谓至关重要。

  • 单栈的多数据抽象——我们的系统支持同一存储栈的三种不同的数据抽象: Blob、Table 和 Queue。由于采用这种设计,所有数据抽象都能使用相同的集群 内和集群间复制,以及负载均衡系统,从而实现改进数据流和分区层所带来的优 点。除此之外,由于 Blob,Table 和 Queue 具有不同的性能需求,我们的单栈方 法可以在同一套硬件上运行所有服务,从而降低成本。Blob 采用大规模磁盘容 量,表格使用一个节点上多个磁盘中的 I/O 轴(所需的容量没有 Blob 多),队列主要使用内存。因此,我们不仅把不同用户的工作负荷平均分摊在共享资源上, 还把 Blob、Table 和 Queue 的流量分摊在同一套存储节点上。
  • 系统定义对象表的利用——我们选择固定数量的系统定义对象表构建 Blob, Table 和 Queue 抽象层,而非将原对象表语义暴露给最终用户。这一决定将我们系统管理变成了只是对于内部模式结构,即系统定义对象表的管理。同时,此决定还可简化维护工作和内部数据结构升级,并将这些系统定义表的变化从最终用户数据抽象层中隔离出来。
  • 提供大量100TB 储存——现在一个账户的储存量不超过100TB。这要求所有存储账户数据需要控制在给定的存储集群内,同时我们最初的存储集群只能容纳两个PB 的原始数据(新型储存构架能容纳20-30PB 数据)。为了在单个数据中心获得 更多的储存空间,客户在该地点需要使用一个以上账户。我们许多大客户(储存 PB 数据)通常用多个账户完成不同区域和不同位置(客户数据的本地访问)的储存,这也合理解释了为什么会出现折中的情况。因此,在给定地点,将他们的数据分割成许多账户以提供更多储存空间,这样更符合他们现存的分区设计。即便如此,这仍然需要大量服务来让账户级别划分逻辑,但是并不是所有的客户在 他们设计的时候都考虑这一点。因此,我们计划增加储存量,以便将来用于给定的存储账户。
  • CAP定理——WAS既有超高的一致性,又有极强的可用性。两者的组合似乎与 CAP定理相悖[2],因为根据该定理,分布式的计算机系统不可能同时具备一致 性、可用性和分区容忍性。但是,在实际操作中,我们的系统却能在一套存储集 群中同时实现这三种属性。我们通过一套特定的故障模型来对我们的系统进行分 层设计,从而实现了这一现象。

流层有一个简单的只追加属性数据模型,当出现网络分区和其他故障时能保证极高的可用性;而建立在流层上的分区层,能确保超强的一致性。这一分层设置能让我们在面临网络分区的时候,将提供强一致性的节点与存储数据的节点分离, 并且可用。分离和定位某一特定故障的功能使得我们的系统能够在实际操作中即 使遭遇各种类型的故障,也能保证强可用性和高一致性。例如,我们在一套存储集群中遇到的网络分区问题类型包括节点失效和 top-of-rack(TOR)开关故障。 如果是 top-of-rack 开关故障,该支架将停止工作——即,数据流层将停止使用该支架,并开始使用其他可用支架的分区,以便继续写入数据流。另外,分区层将重新分配其 RangePartition 到可用支架上的分区服务器上,以便所有数据都能继续可用,同时兼具高可用性和强一致性。因此,我们的系统考虑到了系统中很可能出现的网络分区问题(节点层级以及 TOR 故障),所以设计成能提供强一致性 和高可用性。

  • 高性能调试日志——研发WAS的过程中,我们使用的是可扩展的调试日志架构。 系统将日志写入本地磁盘的存储节点中,并提供一套类似grep(可做文件内的字 符串查找)的应用程序,在所有节点日志中进行分散式搜索。考虑到所记录的日志数据量,我们并不急于输出这些详细的日志。

在 WAS 投入运营时我们曾考虑减少日志以提高性能。但是考虑到详细日志的使用,我们在降低系统日志容量时非常谨慎。相反,自动化的语汇单元化和压缩输出优化了日志系统,这极大地提高了性能并降低了磁盘空间方面的开销,最终我们的系统的日志量达到了 100MB/s,同时每个节点应用性能所受的影响却很小。 这一特性可以将一段时间内整个群详细的日志记录下来。高性能的日志系统和相 应的日志搜索工具是生产过程中能详细调查问题的关键,同时我们不用部署特别代码或重现问题。

  • 压点测试——对出现在某一大型分布式计算机系统中所有复杂行为的所有组合 进行测试很不现实。因此我们借助“压点”来捕捉这些复杂的行为和交互操作。该系统针对我们系统中的所有主要操作以及系统中的所有点提供程控接口,以制造故障。这类压点命令的例子包括:抽点检查 RangePartition、合并一组RangePartition 检查 点、RangePartition 垃圾收集、 切分/合并/负载平衡 RangePartition、纠删编码或纠删码恢复、使某一集群内各种类型的服务器崩溃、 注入网络延迟、注入磁盘延迟等。压点系统能在某些特定或随机命令的压力运行过程中被用来刺激这类交互操作。 该系统还可以从复杂的交互操作中发现问题并对其进行重现,这非常有帮助,而如果任由其自然发生,那可能要耗费好几年的时间。

9. 相关研究(Related Work)

之前的研究[9]表明,要在不良的网络连接环境下实现强一致性和高可用性是个严峻的挑战。有些系统通过降低一致性来提高可用性,以求解决这一问题 [22,14,6]。但这却将负担转移给了应用程序,使其需要处理数据冲突问题。例如, 最初亚马逊 SimpleDB 使用的是最终一致性模型,但最近却增加了强一致性的操作[23]。Van Renesse等[20]已透漏,通过链式复制可以建立大型的存储系统,它们既可以提供高度的一致性,也能保证高度的可用性;之后,这扩展为所有复本上都可以进行读取操作[21]。考虑到我们的顾客需要强一致性,我们着手开发一 套可以同时保证强一致性和高可用性的系统,以及针对我们故障模型的分区容忍性。

和其他具有高可用性的分布式存储系统一样[6,14,1,5],WAS 也提供地理冗余存储。其中有些系统将异地备援放置在动态应用请求的关键路径上,但是,我们却进行了设计折衷,采用的是经典的异步异地复制方式,使其离开关键路径。执行 完全异步异地的复制方式使得我们能为应用程序提供性能更良好的写入延迟,也 极大地优化和提高了交叉数据中心宽带的使用效率,例如异地备援的分批处理和压缩。折衷意味着如果发生灾难,需要使用失效备援,那么在失效备援过程中会 出现不可用现象,而且,顾客账户中最新更新的数据可能丢失。

离我们最接近的系统是 GFS[8,15]和 BigTable[4]。这些版本之间的不同在于:(1) GFS 能保证所有复制品都具备松弛的一致性,但并不保证所有复制品都按位相同,但 WAS 却能保证这一点;(2)BigTable 将多个数据块融合在一个交付日志中,并将其并行写入两个 GFS 文件中,以避免 GFS 出现问题,但是我们发现可以在数据流层中使用日志系统来解决这两个问题;( 3)我们提供一套可扩展的 Blob 存储系统以及批量数据库事务,并将它们融入一个类似于 BigTable 的框架 中。此外,我们还描述了 WAS 根据应用程序的流量需求自动加载、分割和合并 RangePartition 的方法。

10. 总结(Conclusions)

Windows Azure 存储平台为开发基于云的解决方案的开发者提供基本的服务。 WAS 将强一致性、全局分区的命名空间和灾难恢复融合在一起,是其多租户环境下的一个重要的客户功能。利用众多用户的多种尖峰使用模式文件,WAS 在 同一套硬件上运行完全不同的工作负载,这极大地降低了存储花费,因为需要提 供的资源数量大大小于在专用硬件中运行这些工作负载所需要的峰值资源的总量。

如实例所示,三种存储抽象层,即 Blobs、Tables 和 Queues,提供存储机制并对大量应用程序的工作流程进行控制。而且,我们尚未提及使用 WAS 系统所带来的便利。例如,脸谱/推特搜索摄入引擎的最初版本只花费一名工程师两个月的时间即研发成功,并将服务发行上市。该经验是我们服务能力的证明,可以帮助 顾客轻松的研发应用程序,并将其在云端部署。

敬请登陆http://www.microsoft.com/windowsazure/,了解更多有关Windows Azure 和Windows Azure存储平台的信息。

鸣 谢

在此我们特别感谢Geoff Voelker、Greg Ganger和其他佚名评论者,感谢大家对本论文提出的宝贵意见。
同时我们要还感谢 Cosmos(必应存储系统)的创造者:Darren Shakib、Andrew Kadatch、Sam McKelvie、Jim Walsh 和 Jonathan Forbes。五年前我们开始研发 Windows Azure 和 Cosmos,并将其视为我们的集群内复制系统。第四章中叙述 的数据抽象化和只追加基于区间的复制系统即由他们创造。我们增加机制对 Cosmos 进行了扩展,创造出我们的数据流层,从而允许我们提供与分区层和流程操作相协调的极强的一致性,使得我们可以有效地分割/合并分区、档案系统、 纠删码、硬盘主轴反饥饿、读写负载平衡,并实现其他方面的改进。

同时,我们还要感谢研发Windows Azure存储平台过程中的其他贡献者:Maneesh Sah、Matt Hendel、Kavitha Golconda、Jean Ghanem、Joe Giardino、Shuitao Fan、 Justin Yu、Dinesh Haridas、Jay Sreedharan、Monilee Atkinson、Harshawardhan Gadgil、Phaneesh Kuppahalli、Nima Hakami、Maxim Mazeev、Andrei Marinescu、 Garret Buban、Ioan Oltean、Ritesh Kumar、Richard Liu、Rohit Galwankar、 Brihadeeshwar Venkataraman、Jayush Luniya、Serdar Ozler、Karl Hsueh、Ming Fan、 David Goebel、Joy Ganguly、Ishai Ben Aroya、Chun Yuan、Philip Taron、Pradeep Gunda、Ryan Zhang、Shyam Antony、Qi Zhang、Madhav Pandya、Li Tan、Manish Chablani、Amar Gadkari、Haiyong Wang、Hakon Verespej、Ramesh Shankar、Surinder Singh、Ryan Wu、Amruta Machetti、Abhishek Singh Baghel、Vineet Sarda、Alex Nagy、Orit Mazor和Kayla Bunch。

最后,我们还要感谢Amitabh Srivastava、G.S. Rana、Bill Laing、Satya Nadella、 Ray Ozzie以及Windows Azure团队的其他队员,感谢大家的支持。

引 用

[1] J. Baker et al., “Megastore: Providing Scalable, Highly Available Storage for Interactive Services,” in Conf. on Innovative Data Systems Research, 2011.
[2] Eric A. Brewer, “Towards Robust Distributed Systems. (Invited Talk),” in Principles of Distributed Computing, Portland, Oregon, 2000.
[3] M. Burrows, “The Chubby Lock Service for LooselyCoupled Distributed Systems,” in OSDI, 2006.
[4] F. Chang et al., “Bigtable: A Distributed Storage System for Structured Data,” in OSDI, 2006.
[5] B. Cooper et al., “PNUTS: Yahoo!’s Hosted Data Serving Platform,” VLDB, vol. 1, no. 2, 2008.
[6] G. DeCandia et al., “Dynamo: Amazon’s Highly Available Key-value Store,” in SOSP, 2007.
[7] Cristian Estan and George Varghese, “New Directions in Traffic Measurement and Accounting,” in SIGCOMM, 2002.
[8] S. Ghemawat, H. Gobioff, and S.T. Leung, “The Google File System,” in SOSP, 2003.
[9] J. Gray, P. Helland, P. O’Neil, and D. Shasha, “The Dangers of Replication and a Solution,” in SIGMOD, 1996.
[10] Albert Greenberg et al., “VL2: A Scalable and Flexible Data Center Network,” Communications of the ACM, vol. 54, no. 3, pp. 95-104, 2011.
[11] Y. Hu and Q. Yang, “DCD—Disk Caching Disk: A New Approach for Boosting I/O Performance,” in ISCA, 1996.
[12] H.T. Kung and John T. Robinson, “On Optimistic Methods for Concurrency Control,” ACM Transactions on Database Systems, vol. 6, no. 2, pp. 213-226, June 1981.
[13] Leslie Lamport, “The Part-Time Parliament,” ACM Transactions on Computer Systems, vol. 16, no. 2, pp. 133169, May 1998.
[14] A. Malik and P. Lakshman, “Cassandra: a decentralized structured storage system,” SIGOPS Operating System Review, vol. 44, no. 2, 2010.
[15] M. McKusick and S. Quinlan, “GFS: Evolution on Fastforward,” ACM File Systems, vol. 7, no. 7, 2009.
[16] S. Mysore, B. Agrawal, T. Sherwood, N. Shrivastava, and S. Suri, “Profiling over Adaptive Ranges,” in Symposium on Code Generation and Optimization, 2006.
[17] P. O’Neil, E. Cheng, D. Gawlick, and E. O’Neil, “The LogStructured Merge-Tree (LSM-tree),” Acta Informatica – ACTA, vol. 33, no. 4, 1996.
[18] H. Patterson et al., “SnapMirror: File System Based Asynchronous Mirroring for Disaster Recovery,” in USENIX-FAST, 2002.
[19] Irving S. Reed and Gustave Solomon, “Polynomial Codes over Certain Finite Fields,” Journal of the Society for Industrial and Applied Mathematics, vol. 8, no. 2, pp. 300304, 1960.
[20] R. Renesse and F. Schneider, “Chain Replication for Supporting High Throughput and Availability,” in USENIXOSDI, 2004.
[21] J. Terrace and M. Freedman, “Object Storage on CRAQ: High-throughput chain replication for read-mostly workloads,” in USENIX’09, 2009.
[22] D. Terry, K. Petersen M. Theimer, A. Demers, M. Spreitzer, and C. Hauser, “Managing Update Conflicts in Bayou, A Weakly Connected Replicated Storage System,” in ACM SOSP, 1995.
[23] W. Vogel, “All Things Distributed – Choosing Consistency,” in http://www.allthingsdistributed.com/2010/02/strong_consist ency_simpledb.html, 2010.