【seaweedfs】2、Finding a needle in Haystack: Facebook’s photo storage 分布式对象存储论文

文章目录

  • 一、介绍
  • 二、背景、设计理念
    • [2.1 背景](#2.1 背景)
    • [2.2 NFS-based Design](#2.2 NFS-based Design)
    • [2.3 Discussion](#2.3 Discussion)
  • 三、设计和实现
    • [3.1 概览](#3.1 概览)
    • [3.2 Haystack Directory](#3.2 Haystack Directory)
    • [3.3 Haystack Cache](#3.3 Haystack Cache)
    • [3.4 Haystack Store](#3.4 Haystack Store)
      • [3.4.1 Photo Read](#3.4.1 Photo Read)
      • [3.4.2 Photo Write](#3.4.2 Photo Write)
      • [3.4.3 Photo Delete](#3.4.3 Photo Delete)
      • [3.4.4 The Index File](#3.4.4 The Index File)
      • [3.4.5 Filesystem](#3.4.5 Filesystem)
    • [3.5 Recovery from failures](#3.5 Recovery from failures)
    • [3.6 Optimizations](#3.6 Optimizations)
      • [3.6.1 Compaction](#3.6.1 Compaction)
      • [3.6.2 Saving more memory](#3.6.2 Saving more memory)
      • [3.6.3 Batch upload](#3.6.3 Batch upload)
  • 四、Evaluation
    • [4.1 Characterizing photo requests](#4.1 Characterizing photo requests)
      • [4.1.1 Features that drive photo requests](#4.1.1 Features that drive photo requests)
      • [4.1.2 Traffic Volume](#4.1.2 Traffic Volume)
    • [4.2 Haystack Directory](#4.2 Haystack Directory)
    • [4.3 Haystack Cache](#4.3 Haystack Cache)
    • [4.4 Haystack Store](#4.4 Haystack Store)
      • [4.4.1 Experimental setup](#4.4.1 Experimental setup)
      • [4.4.2 Benchmark performance](#4.4.2 Benchmark performance)
      • [4.4.3 Production workload](#4.4.3 Production workload)
  • [五、Related Work](#五、Related Work)
  • 六、Conclusion

论文地址
代码实现
相关资料

摘要:本文描述了一种针对Facebook的Photos应用程序优化的对象存储系统HayStack。Facebook目前存储了超过2600亿张图片,这相当于超过20 PB的数据。用户每周上传10亿张新图片(∼60 TB),而峰值时每秒提供超过100万张图片。

Haystack 提供了一种更便宜的,比我们以前的方法性能更高的,解决方案。我们的主要观察是,传统设计会因为太多的查找元数据而导致过多的磁盘操作,所以我们减少了每张图片的元数据,以便 Haystack storage 可以在主内存中执行所有元数据查找。这可节省读取实际数据的磁盘操作,从而增加了总体吞吐量。

一、介绍

分享图片是Facebook最受欢迎的功能之一。到目前为止,用户已经上传了超过650亿张图片,使Facebook成为最大的图片分享网站在世界上。对于每一张上传的图片,Facebook会生成并存储四张不同大小的图片,其中转化为超过2600亿张图像和20多个数PB的数据。用户上传10亿张新图片(∼60TB),Facebook 每周的服务超过最高峰时每秒一百万张图像。正如我们所期望的那样这些数字将在未来增加,图片存储这对Facebook的基础设施构成了重大挑战。

本文介绍了Facebook的图片存储系统HayStack的设计和实现,该系统在过去的24个月里已经投入使用。Hystack是我们为在Facebook上共享图片而设计的对象存储,其中的数据只写入一次,经常读取,从未修改,也很少删除。我们为图片设计了自己的存储系统,因为传统的文件系统在我们的工作负载下性能很差。

根据我们的经验,我们发现传统的基于POSIX[21]的文件系统的缺点是目录和每个文件的元数据。对于图片,大多数元数据(如权限)都未使用,因此浪费了存储容量。

然而,更大的代价是,为了找到文件本身,必须将文件的元数据从磁盘读取到内存中。虽然在小范围内微不足道,成倍增加数十亿张图片和数PB的数据,但访问元数据是吞吐量的瓶颈。我们发现这是在使用挂载在NFS上的网络连接存储(NAS)设备时的关键问题。读取一张图片需要几次磁盘操作:一次(或通常更多)将文件名转换为索引节点号,另一次从磁盘读取索引节点,最后一次读取文件本身。简而言之,对元数据使用磁盘IO是我们读取吞吐量的限制因素。请注意,在实践中,这个问题会带来额外的成本,因为我们必须依赖CDN来为大多数读取流量提供服务。

考虑到传统方法的缺点,我们设计了HayStack来实现四个主要目标:

  • 高吞吐量和低延迟。我们的图片存储系统必须跟上用户的请求。超出我们处理能力的请求要么被忽略,这对用户体验来说是不可接受的;要么被CDN处理,这是昂贵的,并且达到了回报递减的地步。此外,图片应该快速提供服务,以促进良好的用户体验。Hystack通过每次读取最多需要一次磁盘操作,来实现高吞吐量和低延迟。我们通过将所有元数据保存在主内存中来实现这一点,我们通过极大地减少在磁盘上查找图片所需的每张图片的元数据来实现这一点。
  • 高可用。在大型系统中,故障每天都在发生。我们的用户依赖于他们的图片是可用的,应该不会遇到错误,尽管不可避免的服务器崩溃和硬盘故障。可能会发生整个数据中心断电或跨国链路被切断的情况。Hystack在地理上不同的位置复制了每一张图片。如果我们失去了一台机器,我们会引入另一台来取而代之,在必要时复制数据以实现冗余。
  • 性价比高。与我们以前的基于NFS的方法相比,Hystack的性能更好,成本更低。我们从两个维度量化节省的成本:每TB可用存储的Hystack成本和归一化的每TB可用存储的HayStack读取速率。在HayStack中,每个可用TB的成本比设备上的相同TB低28%,每秒处理的读取数多4倍。
  • 很简单。在生产环境中,我们不能夸大易于实现和维护的设计的力量。由于 Haystack 是一个新系统,缺乏多年的生产级测试,我们特别注意保持它的简单性。这种简单性使我们可以在几个月而不是几年内构建和部署一个工作系统。

这项工作描述了我们从构思到实施每天为数十亿张图像提供服务的生产质量系统的经验。我们的三个主要贡献是:

  • HayStack,一个针对高效存储和检索数十亿张图片进行了优化的对象存储系统。
  • 在构建和扩展廉价、可靠和可用的图片存储系统方面吸取的经验教训。
  • 对向Facebook的图片分享应用发出的请求进行描述。

我们对本文的其余部分进行了如下组织。第2节提供了背景,并重点介绍了我们以前的体系结构中的挑战。我们在第3节描述了HayStack的设计和实现。第4节描述了我们的图片读写工作负载,并证明HayStack满足我们的设计目标。我们与第5节中的相关工作进行了比较,并在第6节中总结了本文。

二、背景、设计理念

在这一节中,我们将描述HayStack之前存在的体系结构,并重点介绍我们学到的主要经验教训。由于空间限制,我们对此设计的讨论忽略了生产级部署的几个细节。

2.1 背景

我们首先简要概述Web服务器、内容交付网络(CDN)和存储系统如何交互以在热门网站上提供图片的典型设计。图1描述了从用户访问包含图像的页面到从其在磁盘上的位置下载该图像的步骤。当访问页面时,用户的浏览器首先向Web服务器发送一个HTTP请求,该服务器负责为浏览器生成要呈现的标记。对于每个图像,Web服务器都会构造一个URL,将浏览器指向下载数据的位置。对于受欢迎的站点,此URL通常指向CDN。如果CDN缓存了镜像,则CDN会立即使用数据进行响应。否则,CDN会检查URL,该URL嵌入了足够的信息,可以从网站的存储系统中检索图片。然后,CDN更新其缓存数据,并将图像发送到用户的浏览器。

2.2 NFS-based Design

在我们的第一个设计中,我们使用基于NFS的方法实现了图片存储系统。虽然这一小节的其余部分提供了关于该设计的更多细节,但我们学到的主要教训是,CDN本身并不提供在社交网站上提供图片的实用解决方案。CDN确实有效地为最热门的图片-头像和最近上传的图片-提供服务,但像Facebook这样的社交网站也会产生大量对不太受欢迎(通常是较老的)内容的请求,我们称之为长尾。来自长尾的请求占我们通信量的很大一部分,几乎所有这些请求都访问支持图片存储主机,因为这些请求通常在CDN中缺失。虽然为这条长尾缓存所有图片会非常方便,但这样做并不划算,因为需要非常大的缓存空间。

我们基于NFS的设计将每张图片存储在一组商业NAS设备上的自己的文件中。一组机器、图片存储服务器,然后通过 NFS挂载这些NAS设备导出的所有卷。图2说明了该体系结构,并显示了处理图像的HTTP请求的图片存储服务器。图片存储服务器从图像的URL中提取文件的卷和完整路径,通过NFS读取数据,并将结果返回给CDN。

最初,我们在一个NFS卷的每个目录中存储了数千个文件,这导致了读取单个图像所需的磁盘操作次数过多。由于NAS设备管理目录元数据的方式,将数千个文件放在一个目录中效率极低,因为该目录的块图太大,设备无法有效地缓存。因此,检索单个图像需要10次以上的磁盘操作是很常见的。在将目录大小减少到每个目录数百个图像之后,所产生的系统通常仍然需要3次磁盘操作来获取图像:一次是从磁盘读目录元数据,第二次是读inode,第三次是读文件内容。

为了进一步减少磁盘操作,我们让 Photo Store 服务器显式缓存 NAS 设备返回的文件句柄。

  • 当第一次读取文件时,图片存储服务器通常会打开文件,但也会将文件名到文件句柄的映射缓存到 Memcache 中。
  • 当请求其文件句柄被缓存的文件时,图片存储服务器使用我们添加到内核中的 由文件句柄打开的 定制系统调用 直接打开该文件。
  • 遗憾的是,有人可能会争辩说,将所有文件句柄存储在 Memcache 中的方法可能是一种可行的解决方案。然而,这只解决了问题的一部分,因为它依赖于 NAS 设备将其所有 inode 都放在主内存中,这对传统文件系统来说是一个昂贵的要求。我们从NAS方法中学到的主要经验是,只关注缓存-无论是NAS设备的缓存还是像Memcache这样的外部缓存-对减少磁盘操作的影响有限。存储系统最终处理对不太受欢迎的图片的请求的长尾,这些图片在CDN中不可用,因此很可能在我们的缓存中遗漏。这种文件句柄缓存只提供了很小的改进,因为不太受欢迎的图片从一开始就不太可能被缓存。

2.3 Discussion

对于何时或何时不构建自定义存储系统,我们很难提供准确的指导方针。然而,我们相信,它仍然有助于社区深入了解我们为什么决定构建HayStack。

面对我们基于NFS的设计中的瓶颈,我们探索了构建类似于GFS的系统是否有用。由于我们将大部分用户数据存储在MySQL数据库中,因此系统中文件的主要使用案例是工程师用于开发工作、日志数据和图片的目录。NAS设备为开发工作和日志数据提供了非常好的性价比。此外,我们用 Hadoop 处理超大的日志数据。在长尾中处理图片请求是一个无论是MySQL、NAS设备还是Hadoop都不能很好解决的问题。

人们可以用我们面临的困境来形容,因为现有的存储系统缺乏正确的RAM与磁盘比率。然而,没有正确的比例。系统只需要足够的主内存,以便可以一次缓存所有文件系统元数据。在我们基于NAS的方法中,一张图片对应一个文件,每个文件至少需要一个inode,它有数百个字节大。在这种方法中拥有足够的主内存并不划算。为了实现更好的性价比,我们决定构建自定义存储系统,以减少每张图片的文件系统元数据量,以便拥有足够的主内存比购买更多的NAS设备更具成本效益。

三、设计和实现

Facebook使用CDN为热门图片提供服务,并利用HayStack高效响应长尾图片请求。当一个网站遇到提供静态内容的I/O瓶颈时,传统的解决方案是使用CDN。CDN承担了足够的负担,以便存储系统可以处理剩余的尾部。在Facebook,CDN必须缓存不合理的大量静态内容,才能使传统的(且廉价的)存储方法不受I/O限制。

认识到在不久的将来CDN不会完全解决我们的问题,我们设计了HayStack来解决我们基于NFS的方法中的关键瓶颈:磁盘操作。我们承认,对不太受欢迎的图片的请求可能需要磁盘操作,但我们的目标是将此类操作的数量限制为仅读取实际图片数据所需的操作。Hystack通过显著减少用于文件系统元数据的内存来实现这一目标,从而使将所有这些元数据保存在主内存中变得可行。

回想一下,每个文件存储一张图片会产生比合理缓存更多的文件系统元数据。Hystack采取了一种简单的方法:它将多张图片存储在一个文件中,因此维护着非常大的文件。我们表明,这种直截了当的方法非常有效。此外,我们认为它的简单性是其优势所在,有助于快速实施和部署。我们现在讨论这项核心技术及其周围的体系结构组件如何提供可靠且可用的存储系统。在下面对HayStack的描述中,我们区分了两种元数据。应用程序元数据描述了构造浏览器可以用来检索图片的URL所需的信息。文件系统元数据标识主机检索驻留在该主机磁盘上的图片所需的数据

3.1 概览

Haystack 结构由3个核心组件组成:Store、Directory、Cache。

  • Store 封装了图片的永久存储系统,是管理图片文件系统元数据的唯一组件。
  • 我们按物理 Volume 组织存储容量。例如,我们可将服务器的10TB容量组织成100个物理 Volume,每个物理 Volume 提供100 GB的存储。我们进一步将不同机器上的物理卷分组为逻辑卷。当HayStack将图片存储在逻辑卷上时,该图片将写入所有相应的物理卷。这种冗余使我们能够减少由于硬盘驱动器故障、磁盘控制器错误等造成的数据丢失。
  • Directory 维护逻辑到物理的映射以及其他应用程序元数据,例如每张图片所在的逻辑卷和具有可用空间的逻辑卷。
  • Cache 充当我们的内部CDN,它保护存储免受对最受欢迎图片的请求,并在上游CDN节点出现故障需要重新获取内容时提供隔离。

图3说明了 Store、Directory、Cache 组件如何适应用户的浏览器、Web服务器、CDN和存储系统之间的规范交互。在 Haystack 架构中,浏览器可以定向到CDN或缓存。请注意,虽然缓存本质上是一个CDN,但为了避免混淆,我们使用'CDN'表示外部系统,使用'Cache'表示缓存图片的内部系统。拥有内部缓存基础设施使我们能够减少对外部CDN的依赖。当用户访问页面时,Web服务器使用 Directory 为每张图片构造一个URL。URL包含多条信息,每条信息对应于从用户浏览器联系CDN(或缓存)到最终从商店中的机器检索图片的一系列步骤。将浏览器指向CDN的典型URL为 http://(CDN)/(Cache)/(Machine id)/(Logical volume, Photo)

URL的第一部分指定向哪个CDN请求图片。CDN可以只使用URL的最后一部分在内部查找图片:逻辑卷和图片ID。如果CDN无法找到图片,则它会从URL中剥离CDN地址,并联系缓存。缓存执行类似的查找来找到图片,如果未命中,则从URL中剥离缓存地址,并从指定的Store机器请求图片。直接进入缓存的图片请求具有类似的工作流程,只是URL缺少CDN特定信息。

图4显示了HayStack中的上传路径。当用户上传照片时,她首先将数据发送到网络服务器。接下来,该服务器从 Directory 请求启用写入的逻辑卷。最后,Web服务器为照片分配唯一ID,并将其上载到映射到分配的逻辑卷的每个物理卷

3.2 Haystack Directory

该 Directory 有四个主要职能。

  • 首先,它提供了从逻辑卷到物理卷的映射。Web服务器在上传照片和构建页面请求的图像URL时使用此映射。
  • 其次,目录跨逻辑卷对写操作,和跨物理卷的读操作,进行负载平衡。
  • 第三,目录确定照片请求应该由CDN还是由缓存来处理。此功能使我们可以调整对CDN的依赖。
  • 第四,目录标识那些由于操作原因或因这些卷已达到其存储容量而为只读的逻辑卷。为便于操作,我们在机器粒度上将卷标记为只读。

当我们通过添加新机器来增加存储区的容量时,这些机器是启用写入的;只有启用写入的机器才会接收上载。随着时间的推移,这些机器上的可用容量会减少。当机器耗尽其容量时,我们将其标记为只读。在下一个小节中,我们将讨论这种区别如何对缓存和存储产生微妙的影响。

目录是一个相对简单的组件,它将其信息存储在一个复制的数据库中,该数据库通过利用 Memcache 来减少延迟的 PHP 接口访问。如果存储机器上的数据丢失,我们将删除映射中的相应条目,并在新的存储机器上线时替换它。

3.3 Haystack Cache

Cache 从CDNs和直接从用户的浏览器接收对照片的HTTP请求。我们将缓存组织为分布式哈希表,并使用照片的ID作为关键字来定位缓存的数据。如果缓存不能立即响应请求,则缓存从URL中标识的 Store机器 获取图片,并根据需要回复CDN或用户的浏览器。

现在我们重点介绍缓存的一个重要行为方面。它只在满足两个条件时缓存照片:(A)请求直接来自用户,而不是CDN;以及(B)照片从可写存储机器获取。

  • 第一种情况的理由是,我们使用基于NFS的设计的经验表明,后CDN缓存是无效的,因为在CDN中未命中的请求不太可能命中我们的内部缓存。
  • 第二个理由是间接的。我们使用缓存来保护支持写的存储机器不被读取,因为有两个有趣的特性:照片在上传后不久访问最频繁,而我们工作负载的文件系统通常在执行读取或写入时性能更好,但不能同时执行这两种操作(第4.1节)。因此,如果没有缓存,启用写入的存储机器将看到最多的读取。鉴于这一特点,我们计划实施的一个优化是主动将最近上传的照片推送到缓存中,因为我们希望这些照片很快就会被经常阅读。

3.4 Haystack Store

存储机器的界面特意是基本的。读取会发出非常具体且包含良好的请求,请求具有给定ID的照片、特定逻辑卷以及来自特定物理存储机器的照片。如果找到照片,机器会将其返回。否则,机器将返回错误。

每台存储机器管理多个物理卷。每一卷都有数百万张照片。具体来说,读者可以将物理卷看作是一个非常大的文件(100 GB),保存为 /hay/haystack<逻辑卷id>。仅使用相应逻辑卷的ID和照片所在的文件偏移量,存储机器就可以快速访问照片。这一知识是HayStack设计的基础:检索特定照片的文件名、偏移量和大小,而不需要磁盘操作。存储机为其管理的每个物理卷保存打开的文件描述符,并在内存中将照片ID映射到文件系统元数据(即,文件、偏移量和大小,以字节为单位),这些元数据对于检索照片至关重要。

我们现在描述每个物理卷的布局,以及如何从该卷派生内存中映射。存储机将物理卷表示为一个大文件,该文件由一个超级块和一系列 needle(针) 组成。每一根针代表一张储存在 Haystack 中的照片。图5说明了每根针的卷文件和格式。表1描述了每个针中的字段

为了快速检索针,每台存储机器都为其每个卷维护一个内存中的数据结构。该数据结构将(key,Alternate Key)2对映射到相应的针的标志,大小为2。由于历史原因,照片的id对应于Key,而其类型用于Alternate Key。在上传过程中,网络服务器将每张照片缩放成四种不同的大小(或类型),并将它们存储为不同的针,但使用相同的密钥。这些字节之间的重要区别以及卷偏移量。崩溃后,存储机器可以在处理请求之前直接从卷文件重建此映射。现在,我们将介绍Store机器如何在响应读取、写入和删除请求(Store支持的唯一操作)的同时维护其卷和内存映射。

3.4.1 Photo Read

当缓存机器请求照片时,它向存储机器提供逻辑卷ID、密钥、备用密钥和Cookie。Cookie是嵌入到照片URL中的数字。在上传照片时,Cookie的值由目录随机分配并存储在目录中。该Cookie有效地消除了旨在猜测照片的有效URL的攻击。

当Store机器收到来自缓存机器的照片请求时,Store机器会在其内存映射中查找相关的元数据。如果照片尚未删除,存储机器将在卷文件中查找适当的偏移量,从磁盘读取整个指针(其大小可以提前计算),并验证Cookie和数据的完整性。如果这些检查通过,则存储机器将照片返回到缓存机器

3.4.2 Photo Write

在将照片上传到HayStack时,Web服务器向存储机器提供逻辑卷ID、密钥、备用密钥、Cookie和数据。每台机器同步地将针图像附加到其物理卷文件中,并根据需要更新内存中的映射。

虽然简单,但这种仅限附加的限制会使一些修改照片的操作复杂化,例如旋转。由于HayStack不允许覆盖针,因此只能通过添加具有相同键和备用键的更新针来修改照片。如果新的指针被写入与原始指针不同的逻辑卷,则目录将更新其应用程序元数据,并且以后的请求将永远不会获取较旧的版本。如果将新指针写入相同的逻辑卷,则存储机器会将新指针附加到相同的相应物理卷。Haystack 根据它们的偏移量来区分这种重复的针。也就是说,物理卷内的最新版本的针是偏移量最高的那个。

3.4.3 Photo Delete

删除照片很简单。存储机器在内存映射中和在卷文件中同步设置删除标志。获取删除照片的请求首先检查内存中的标志,如果该标志被启用,则返回错误。请注意,被删除的针占用的空间暂时会丢失。随后,我们讨论了如何通过压缩卷文件来回收已删除的针空间。

3.4.4 The Index File

存储机器在重启时使用一个重要的优化--索引文件。虽然理论上机器可以通过读取其所有物理卷来重建其内存中的映射,但这样做非常耗时,因为必须从磁盘读取所有数据量(TB)。索引文件允许存储机器快速构建其内存映射,从而缩短重新启动时间。

存储机器为它们的每个卷维护一个索引文件。索引文件是用于在磁盘上高效定位针的内存数据结构的检查点。索引文件的布局类似于卷文件的布局,包含一个超级块,后跟与超级块中的每个指针对应的一系列索引记录。这些记录的出现顺序必须与相应针在卷文件中出现的顺序相同。图6说明了索引文件的布局,表2说明了每条记录中的不同字段。

重新开始使用索引比仅仅读取索引和初始化内存中的映射稍微复杂一些。出现这种复杂情况是因为索引文件是异步更新的,这意味着索引文件可能代表过时的检查点。当我们写一张新照片时,Store机器「同步」地将一个指针附加到卷文件的末尾,并「异步」地将一条记录附加到索引文件。当我们删除一张照片时,Store机器会在不更新索引文件的情况下同步设置该照片的指针中的标志。这些设计决策使写入和删除操作能够更快地返回,因为它们避免了额外的同步磁盘写入。它们还造成了两个我们必须解决的副作用:针可以在没有相应索引记录的情况下存在,并且索引记录不会反映删除的照片。

我们将没有相应索引记录的针称为孤儿。在重新启动期间,存储机器会按顺序检查每个孤立项,创建匹配的索引记录,并将该记录附加到索引文件中。请注意,我们可以「快速识别」孤立对象,因为索引文件中的最后一条记录对应于卷文件中的最后一个非孤立指针。为了完成重新启动,Store机器 现在仅使用索引文件 来初始化其内存映射。

由于索引记录不反映已删除的照片,因此存储机器可以检索实际上已被删除的照片。为了解决这个问题,在 Store 机器读取照片的整个针之后,该机器可以检查删除的标志。如果指针被标记为已删除,则存储机器会相应地更新其内存中映射,并通知缓存未找到该对象。

3.4.5 Filesystem

我们将 HayStack 描述为使用通用的类Unix文件系统的对象存储,但某些文件系统比其他文件系统更适合HayStack。特别是,存储机器应该使用不需要太多内存的文件系统,以便能够在大文件中快速执行随机查找。目前,每台存储机器都使用XFS,这是一种基于区的文件系统。对于HayStack,XFS有两个主要优势。

  • 首先,几个连续的大文件的块图可以足够小,可以存储在主存储器中。
  • 其次,XFS提供了高效的文件预分配,减少了碎片,并控制了块映射的大小。

使用XFS,HayStack可以在读取照片时消除检索文件系统元数据的磁盘操作。然而,这一好处并不意味着HayStack可以保证每次读取照片都会导致一次磁盘操作。当照片数据跨越区段或RAID边界时,存在文件系统需要多个磁盘操作的特殊情况。Haystack 预分配1 GB的扩展区,并使用256 KB的RAID条带大小,因此在实践中我们很少遇到这样的情况。

3.5 Recovery from failures

像许多在商用硬件上运行的其他大型系统一样,HayStack需要容忍各种故障:故障硬盘驱动器、行为不正常的RAID控制器、损坏的主板等。我们使用两种简单的技术来容忍故障-一种用于检测,另一种用于修复。

为了主动发现有问题的商店机器,我们维护一个称为干草叉的后台任务,该任务定期检查每台商店机器的运行状况。Pitchfork远程测试到每个存储计算机的连接,检查每个卷文件的可用性,并尝试从存储计算机读取数据。如果Shochfork确定某个存储计算机始终未能通过这些运行状况检查,则它会自动将驻留在该存储计算机上的所有逻辑卷标记为只读。我们脱机手动解决检查失败的根本原因。

一旦诊断出来,我们可能很快就能解决这个问题。有时,这种情况需要更繁重的批量同步操作,在此操作中,我们使用副本提供的卷文件重置存储计算机的数据。批量同步很少发生(每个月只有几次),而且简单,尽管执行速度很慢。主要瓶颈是,要批量同步的数据量通常比每台Store计算机上的NIC速度高几个数量级,导致平均恢复时间长达数小时。我们正在积极探索解决这一限制的技术。

3.6 Optimizations

我们现在讨论对HayStack的成功非常重要的几个优化。

3.6.1 Compaction

压缩是一种在线操作,它回收已删除和重复的针(具有相同键和备用键的针)所使用的空间.存储机器通过将针复制到新文件中,同时跳过任何重复或删除的条目来压缩卷文件. 在压缩过程中,删除操作将同时删除这两个文件.一旦此过程到达文件末尾,它就会阻止对卷的任何进一步修改,并自动交换文件和内存中的结构

我们使用压缩来从删除的照片中释放空间。删除的模式类似于照片查看:年轻的照片更有可能被删除。在一年的时间里,大约25%的照片被删除。

3.6.2 Saving more memory

如上所述,存储机器维护包括标志的内存中数据结构,但我们当前的系统仅使用标志字段将针标记为已删除。我们通过将已删除照片的偏移量设置为0来消除对标志在内存中表示的需要。此外,存储机器不会跟踪主内存中的Cookie值,而是在从磁盘读取指针后检查提供的Cookie。通过这两种技术,存储机器的主内存占用量减少了20%。

目前,HayStack平均每张照片使用10字节的内存。回想一下,我们将每个上传的图像缩放为四张照片,所有照片都具有相同的密钥(64位)、不同的备用密钥(32位),因此数据大小也不同(16位)。除了这32个字节之外,由于哈希表的原因,HayStack在每个图像上消耗了大约2个字节的开销,使同一图像的四个缩放照片的总开销达到40个字节。作为比较,假设Linux中的XFS inode t结构为536字节。

3.6.3 Batch upload

由于磁盘通常更擅长执行大型顺序写入,而不是小型随机写入,因此我们尽可能将上传批量放在一起。幸运的是,许多用户将整个相册上传到Facebook,而不是单一的照片,这显然提供了一个将相册中的照片批量放在一起的机会。我们在第4节中量化了将写入聚合在一起的改进。

四、Evaluation

我们将我们的评估分为四个部分.首先,我们对脸书看到的照片请求进行了描述在第二和第三部分中,我们分别展示了目录和缓存的有效性.最后,我们分析了商店使用合成工作负载和生产工作负载时的表现.

4.1 Characterizing photo requests

照片是用户在Facebook上分享的主要内容之一。用户每天上传数百万张照片,最近上传的照片往往比旧照片更受欢迎。图7说明了每张照片作为照片年龄函数的受欢迎程度。为了理解图表的形状,讨论是什么推动了Facebook的照片请求是很有用的

4.1.1 Features that drive photo requests

Facebook 98%的照片请求来自两个功能:新闻订阅和相册。News Feed功能向用户显示他们的朋友分享的最新内容。相册功能允许用户浏览她朋友的照片。她可以查看最近上传的照片,还可以浏览所有个人相册。Facebook 98%的照片请求来自两个功能:新闻订阅和相册。新闻源功能向用户显示他们的朋友分享的最新内容。相册功能允许用户浏览她朋友的照片.她可以查看最近上传的照片,还可以浏览所有个人相册.

图7显示了对几天前的照片的请求急剧上升。新闻提要推动了最近照片的大部分流量,大约在两天左右,当许多故事不再显示在默认的提要视图中时,流量急剧下降。从这个数字来看,有两个关键点值得注意。首先,受欢迎程度的快速下降表明,CDN和缓存中的缓存对于托管流行内容非常有效。其次,该图有一个长尾,这意味着使用缓存数据无法处理大量请求。

4.1.2 Traffic Volume

表3显示了Facebook上的图片流量。由于我们的应用程序将每个图像缩放为4个大小,并将每个大小保存在3个不同的位置,因此写入的HayStack照片数量是上传照片数量的12倍。该表显示,在CDN发出的所有照片请求中,约有10%得到了HayStack的响应。观察到,查看的照片中大部分都是较小的图像。这一特点强调了我们将元数据开销降至最低的愿望,因为低效可能会很快累积起来。此外,对于Facebook来说,阅读较小的图像通常是一种对延迟更敏感的操作,因为它们显示在News Feed中,而较大的图像显示在相册中,可以预取以隐藏延迟。

4.2 Haystack Directory

干草堆栈目录平衡了所有干草堆栈存储机器的读写操作。如图8所示,目录分发读写的直截了当的散列策略非常有效。该图显示了同时部署到生产中的9台不同存储计算机所看到的多写操作数量。这些盒子中的每一个都存储着一组不同的照片。由于这些行几乎无法区分,我们得出结论,《目录》的书写平衡得很好。比较存储计算机之间的读取流量也显示出类似的良好平衡行为。

4.3 Haystack Cache

图9显示了HayStack缓存的命中率。回想一下,仅当照片保存在支持写的Store计算机上时,缓存才会存储照片。这些照片是相对较新的,这解释了高命中率约80%。由于启用写入的存储计算机也会看到最多的读取次数,因此缓存可以有效地显著降低受影响最大的计算机的读取请求率。

4.4 Haystack Store

回想一下,HayStack的目标是照片请求的长尾,并旨在保持高吞吐量和低延迟,尽管看起来是随机读取。我们展示了存储机器在合成和生产工作负载下的性能结果。

4.4.1 Experimental setup

我们在商用存储刀片上部署存储机器。2U存储刀片的典型硬件配置具有2个超线程四核Intel Xeon CPU、48 GB内存、一个带有256-512MB NVRAM的硬件RAID控制器和12个1TB SATA驱动器。

每个存储刀片提供约9TB的容量,配置为由硬件RAID控制器管理的RAID-6分区。RAID-6可提供足够的冗余和出色的读取性能,同时降低存储成本。控制器的NVRAM回写式高速缓存缓解了RAID-6的S降低的写入性能。由于我们的经验表明,在存储计算机上缓存照片是无效的,因此我们完全保留NVRAM以供写入。我们还禁用 disk cache,以确保在崩溃或断电时的数据一致性。

4.4.2 Benchmark performance

我们使用两个基准来评估Store机器的性能:Randomio 和 HayStress。Randomio是一个开源的多线程磁盘I/O程序,我们用它来测量存储设备的原始容量。它发出随机的64KB读取,使用直接I/O发出扇区一致的请求,并报告最大可持续吞吐量。我们使用Randomio来建立读取吞吐量的基准,我们可以将其与其他基准测试的结果进行比较。

HayStress是一个定制的多线程程序,我们使用它来评估针对各种合成工作负载的Store机器。它通过HTTP与存储机器通信(就像缓存一样),并评估存储机器可以维持的最大读写吞吐量。HayStress对一大组虚拟映像执行随机读取,以减少机器缓冲区缓存的影响;也就是说,几乎所有读取都需要磁盘操作。在本文中,我们使用七种不同的HayStress工作负载来评估存储机器。

表4描述了在我们的基准测试下,存储机器可以承受的读写吞吐量和相关延迟。工作负载A对具有201个卷的存储计算机上的64KB映像执行随机读取。结果表明,干草堆栈提供了设备85%的原始吞吐量,而延迟仅高出17%。

我们将存储机的开销归因于四个因素:

  • 它在文件系统之上运行,而不是直接访问磁盘;
  • 磁盘读取大于64KB,因为需要读取整个磁针;
  • 存储的映像可能不与底层的RAID-6设备条带大小对齐,因此从多个磁盘读取一小部分映像;
  • HayStack服务器的CPU开销(索引访问、校验和计算等)。

在工作负载B中,我们再次检查只读工作负载,但更改了70%的读取,以便它们请求更小的图像(8KB而不是64KB)。在实践中,我们发现大多数请求不是对最大尺寸的图像(如相册中所示),而是对缩略图和个人资料照片的请求。

工作负载C、D和E显示存储计算机的写入吞吐量。回想一下,HayStack可以一起成批写入。工作负载C、D和E组1、4和16分别写入单个多次写入。该表显示,在4个和16个映像上摊销写入的固定成本可将吞吐量分别提高30%和78%。正如预期的那样,这也减少了每个映像的延迟。

最后,我们来看看同时存在读操作和写操作时的性能。工作负载F使用98%的读取和2%的多次写入的混合,而G使用96%的读取和4%的多次写入的混合,其中每次多次写入写入16个映像。这些比率反映了生产中经常观察到的情况。该表显示,即使在存在写入的情况下,该存储区也可提供高读取吞吐量。

4.4.3 Production workload

这一部分考察了Store在生产机器上的性能。如第3节所述,有两种类型的存储区--启用写入和只读。支持写的主机服务于读写请求,只读主机服务于读请求。由于这两个类别具有相当不同的流量特征,因此我们分析每个类别中的一组机器。所有机器都具有相同的硬件配置。

以每秒的粒度来看,Store Box看到的照片读写操作量可能会出现很大的峰值。为了即使在出现这些峰值的情况下也能确保合理的延迟,我们保守地分配了大量支持写操作的计算机,以便它们的平均利用率较低。

图10显示了只读和支持写的Store机器上不同类型的操作的频率。请注意,我们在周日和周一看到了照片上传的高峰期,本周剩下的时间会平稳下降,直到周四到周六才趋于平稳。然后,新的星期天到来了,我们达到了每周的新高峰。一般来说,我们的足迹每天增长0.2%到0.5%。

如第3节所述,在生产计算机上,对存储区的写入操作始终是多次写入,以摊销写入操作的固定成本。查找图像组相当简单,因为每张照片的4个不同大小的照片都存储在HayStack中。用户将一批照片上传到相册中也很常见。作为这两个因素的组合,对于这台支持写入的机器,每次多次写入的平均映像数为9.27。

第4.1.2节解释说,最近上传的照片的阅读率和删除率都很高,而且会随着时间的推移而下降。在图10中也可以观察到这种行为;启用写的机器会看到更多的请求(即使一些读流量由缓存提供服务)。

另一个值得注意的趋势是:随着更多的数据被写入到支持写入的框中,照片的数量也会增加,从而导致读取请求率的提高。图11显示了同一时间段内在与图10相同的两台机器上执行读操作和多写操作的延迟。

即使业务量变化很大,多写操作的延迟也相当低(在1到2毫秒之间)且稳定。干草堆机器有一个NVRAM支持的RAID控制器,可以为我们缓冲写入。如第3节所述,NVRAM允许我们异步写指针,然后在多写操作完成后发出单个fsync来刷新卷文件。多次写入延迟非常平稳且稳定。


只读机箱上的读取延迟也相当稳定,即使流量变化很大(在3周内最高可达3倍)。对于启用写入的计算机,读取性能受三个主要因素的影响。

  • 首先,随着存储在机器上的照片数量的增加,该机器的读取流量也会增加(对比图10中每周的流量)。
  • 其次,启用写功能的机器上的照片被缓存在缓存中,而对于只读机器3,它们不被缓存。这表明缓冲区缓存对于只读机器更有效。
  • 第三,最近写的照片通常会立即被重读,因为Facebook突出了最近的内容。在启用写入的框上的这种读取将始终命中缓冲区高速缓存,并提高缓冲区高速缓存的命中率。图中线条的形状是这三个因素共同作用的结果。

存储计算机上的CPU利用率较低。CPU空闲时间在92-96%之间变化。

五、Related Work

据我们所知,HayStack的目标是一个新的设计点,专注于一家大型社交网站看到的照片请求的长尾。

  • 文件系统:HayStack采用了日志结构的文件系统[23],Rosenblum和Ousterhout设计这种文件系统是为了优化写吞吐量,因为大多数读操作都可以从缓存中获取。虽然测量结果[3]和模拟结果[6]表明日志结构文件系统在本地文件系统中还没有充分发挥其潜力,但其核心思想与HayStack非常相关。照片被附加到HayStack Store中的物理卷文件中,HayStack缓存保护已启用写入的计算机不会被最近上载数据的请求速率淹没。主要区别在于:(A)HayStack存储机器写入数据的方式是,一旦数据变为只读,它们就可以有效地为读取提供服务;(B)较旧数据的读取请求速率会随着时间的推移而降低。

一些著作[8,19,28]提出了如何更有效地管理小文件和元数据。这些贡献的共同主线是如何智能地将相关文件和元数据分组在一起。Hystack避免了这些问题,因为它在主内存中维护元数据,并且用户经常批量上传相关照片。

  • 基于对象的存储:HayStack的体系结构与Gibson等人提出的对象存储系统有许多相似之处。[10]在网络连接安全磁盘(NASD)中。在将逻辑存储单元与物理存储单元分开的NASD中,HayStack目录和存储可能分别最类似于文件和存储管理器的概念。在OBFS[25]中,Wang et al.构建一个用户级的基于对象的文件系统,其大小是XFS的1/25。尽管OBFS实现了比XFS更大的写吞吐量,但它的读吞吐量(HayStack的主要关注点)略差一些。

  • 管理元数据:Weil等人。[26,27]解决Ceph1 PB级对象存储中的缩放元数据管理问题。Cave通过引入生成函数而不是显式映射,进一步解耦了从逻辑单元到物理单元的映射。客户端可以计算适当的元数据,而不是查找它。在干草堆栈中实现这一技术仍是未来的工作。Hendricks et.Al[13]注意到,传统的元数据预取算法对对象存储的效率较低,因为由唯一编号标识的相关对象缺乏目录隐式施加的语义分组。他们的解决方案是将对象间关系嵌入到对象ID中。这个想法与HayStack是正交的,因为Facebook将这些语义关系明确地存储为社交图的一部分。在SpyGlass[15]中,Leung et al.提出了一种大规模存储系统元数据快速可扩展搜索的设计方案。Manber和Wu还提出了一种快速搜索整个文件系统的方法[17]。Patil等人。[20]使用GIGA+中的复杂算法来管理与每个目录的数十亿个文件相关联的元数据。我们设计了一个比许多现有工作更简单的解决方案,因为HayStack既不需要提供搜索功能,也不需要提供传统的UNIX文件系统语义。

  • 分布式文件系统:HayStack的逻辑卷概念类似于Lee和Thekkath在Petal中的[14]虚拟磁盘。Boxwood项目[16]探索了使用高级数据结构作为存储的基础。虽然对于更复杂的算法来说很有吸引力,但像B树这样的抽象可能不会对HayStack故意精简的接口和语义产生很大影响。同样,Sinfonia的[1]个小事务和PNUTS的[5]个数据库功能提供了比HayStack需要的更多的功能和更强大的保证。Ghemawat等人。[9]针对主要由追加操作和大量顺序读取组成的工作负载,设计了Google文件系统。BigTable[4]为结构化数据提供了存储系统,并为Google的许多项目提供了类似数据库的功能。目前还不清楚,这些功能中的许多在一个针对照片存储进行优化的系统中是否有意义。

六、Conclusion

本文描述了HayStack,这是一个为Facebook的Photos应用程序设计的对象存储系统。我们设计HayStack是为了服务于在大型社交网络中分享照片所看到的长尾请求。关键的洞察力是在访问元数据时避免磁盘操作。Hystack为照片存储提供了一种容错且简单的解决方案,与使用NAS设备的传统方法相比,成本大大降低,吞吐量更高。此外,HayStack是可增量扩展的,这是我们的用户每周上传数亿张照片所必需的品质。

相关推荐
做梦敲代码14 分钟前
达梦数据库-读写分离集群部署
数据库·达梦数据库
小蜗牛慢慢爬行1 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
hanbarger1 小时前
nosql,Redis,minio,elasticsearch
数据库·redis·nosql
微服务 spring cloud1 小时前
配置PostgreSQL用于集成测试的步骤
数据库·postgresql·集成测试
先睡1 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
弗罗里达老大爷1 小时前
Redis
数据库·redis·缓存
仰望大佬0072 小时前
Avalonia实例实战五:Carousel自动轮播图
数据库·microsoft·c#
学不透java不改名2 小时前
sqlalchemy连接dm8 get_columns BIGINT VARCHAR字段不显示
数据库
一只路过的猫咪2 小时前
thinkphp6使用MongoDB多个数据,聚合查询的坑
数据库·mongodb