0.摘要
**Haystack,一个针对Facebook Photos应用程序优化的对象存储系统。该方法利用NFS上的网络连接存储设备。由于元数据查找,这种传统设计会导致过多的磁盘操作。比如读取一张照片需要几个磁盘操作:
**
- 将文件名转换为索引节点号;
- 从磁盘读取索引节点;
- 读取文件本身。
减少了每个照片的元数据,以便可以在内存中执行所有元数据查找。这种选择节省了用于读取实际数据的磁盘操作,从而提高了总体吞吐量。
1.基于NFS的设计
我们最初在NFS卷的每个目录中存储了数千个文件,这导致读取单个映像的磁盘操作数量过多。由于NAS设备管理目录元数据的方式,将数千个文件放在一个目录中效率极低,因为目录的块图太大,设备无法有效缓存。因此,检索单个映像通常需要10次以上的磁盘操作。
在将目录大小减少到每个目录数百个映像后,生成的系统通常仍会执行3个磁盘操作来获取映像:一个将目录元数据读入内存,第二个将索引节点加载到内存,第三个读取文件内容。
为了进一步减少磁盘操作,我们让照片存储服务器显式缓存NAS设备返回的文件句柄。第一次读取文件时,照片存储服务器会正常打开文件,但也会将文件名到文件句柄的映射缓存在memcache中。当请求文件句柄被缓存的文件时,照片存储服务器直接使用我们添加到内核的自定义系统调用打开文件句柄。遗憾的是,这个文件句柄缓存只提供了一个小的改进,因为不太可能从一开始就缓存不太突出的照片。
每个文件存储一张照片会导致文件系统元数据超过合理缓存的数量。Haystack采用了一种简单的方法:它将多张照片存储在一个文件中,因此可以维护非常大的文件。我们表明,这种直截了当的方法非常有效。此外,我们认为它的简单性是它的优势,有助于快速实施和部署。
2.设计与实现
Haystack架构由3个核心组件组成:HaystackStore、HaystackDirectory和HaystackCache。
当用户访问页面时,web服务器使用目录为每张照片构建URL。URL包含多条信息,每一条信息对应于从用户浏览器接触CDN(或缓存)到最终从商店中的机器检索照片的一系列步骤。将浏览器指向CDN的典型URL如下所示:
URL的第一部分指定从哪个CDN请求照片。CDN可以仅使用URL的最后一部分(逻辑卷和照片id)在内部查找照片。如果CDN无法找到照片,则会从URL中剥离CDN地址并联系缓存。缓存执行类似的查找以查找照片,如果未找到,则从URL中删除缓存地址,并从指定的存储机器请求照片。直接进入缓存的照片请求具有类似的工作流,只是URL缺少特定于CDN的信息。
Haystack中的上传路径。当用户上传照片时,她首先将数据发送到web服务器。接下来,该服务器从目录请求一个已启用写的逻辑卷。最后,web服务器为照片分配一个唯一的id,并将其上载到映射到分配的逻辑卷的每个物理卷。
2.1 Haystack Directory
它将其信息存储在一个复制数据库中,该数据库通过利用memcache以减少延迟。如果我们丢失了存储机器上的数据,我们将删除映射中的相应条目,并在新存储机器联机时进行替换。
主要有四个功能:
-
它提供了从逻辑卷到物理卷的映射。Web服务器在上传照片以及为页面请求构建图像URL时使用此映射。
-
目录负载平衡跨逻辑卷的写入和跨物理卷的读取。
-
目录确定照片请求应该由CDN还是由缓存处理。该功能允许我们调整对CDN的依赖。
-
目录识别那些由于操作原因或由于这些卷已达到其存储容量而只读的逻辑卷。为了便于操作,我们在机器粒度上将卷标记为只读。
2.2 Haystack Cache
接收来自CDN以及直接来自用户浏览器的照片HTTP请求。我们将缓存组织为分布式哈希表,并使用照片的id作为查找缓存数据的键。如果缓存无法立即响应请求,则缓存将从URL中标识的存储机器中提取照片,并根据情况回复CDN或用户浏览器
只有满足以下两个条件时,它才会缓存照片:(a)请求直接来自用户,而不是CDN;(b)照片是从启用写入的存储机器获取的。
我们计划实现的优化是主动将最近加载的照片推送到缓存中,因为我们希望这些照片很快就会被读取。
2.3 Haystack Store
每个存储机器管理多个物理卷。每卷包含数百万张照片。具体而言,读者可以将物理卷视为一个非常大的文件(100GB),保存为“/hay/haystack<logical volume id>”。存储机器可以仅使用相应逻辑卷的id和照片所在的文件偏移量快速访问照片。
检索特定照片的文件名、偏移量和大小,而无需磁盘操作。存储计算机保持打开的文件描述符,并在内存中保持照片id到文件系统元数据(即文件、偏移量和字节大小)的映射,这对检索照片至关重要。
存储机器将物理卷表示为一个大文件,由一个超级块和一系列指针组成。每根针代表一张存储在Haystack中的照片。为了快速检索针头,每台存储机器都为其每卷存储一个内存数据结构。该数据结构将成对的(键、交替键)2映射到相应的针的标志,大小为字节和卷偏移量。崩溃后,存储计算机可以在处理请求之前直接从卷文件重建此映射。我们现在描述存储机器如何在响应读、写和删除请求(存储支持的唯一操作)时维护其卷和内存映射。
2.4 文件系统
目前,每台存储机器都使用XFS,一种基于数据块的文件系统。XFS对于Haystack有两个主要优势。首先,几个连续的大文件的块映射可以足够小,可以存储在主内存中。其次,XFS提供了高效的文件预分配,减少了碎片,并控制了块映射的增长。
使用XFS,Haystack可以消除读取照片时检索文件系统元数据的磁盘操作。然而,这一好处并不意味着Haystack可以保证每次读取照片都会引发一次磁盘操作。当照片数据跨越扩展区或RAID边界时,存在文件系统需要多个磁盘操作的情况。Haystack预先分配了1GB的数据块,并使用256KB的RAID条带大小,因此在实践中我们很少遇到这种情况。
3 其他优化
3.1 压缩
压缩是一种在线操作,可回收已删除和重复的针(具有相同键和备用键的针)所使用的空间。存储机器通过将指针复制到新文件中来压缩卷文件,同时跳过任何重复或删除的条目。压缩过程中,两个文件都会被删除。一旦此过程到达文件末尾,它将阻止对卷的任何进一步修改,并自动交换文件和内存结构。
我们使用压缩来从删除的photos中释放空间。删除模式类似于照片视图:年轻照片更容易被删除。在一年中,大约25%的照片被删除。
3.2 节省更多内存
通过将已删除照片的偏移量设置为0来标记。此外,存储机器不会跟踪主内存中的cookie值,而是在从磁盘读取指针后检查提供的cookie。通过这两种技术,存储机器的主内存占用减少了20%。
目前,Haystack平均每张照片使用10字节的主内存。回想一下,我们将每个上传的图像缩放为四张照片,所有照片都具有相同的密钥(64位),不同的备用密钥(32位),因此数据大小不同(16位)。除了这32个字节之外,Haystack由于哈希表的原因,在每个图像的开销中消耗了大约2个字节,使同一图像的四张缩放照片的总数达到40个字节。为了比较,考虑Linux中的xfs索引节点t结构是536字节。
3.3 批量上传
由于磁盘通常更擅长执行大的顺序写入,而不是小的随机写入,因此我们尽可能一起批量上传。幸运的是,许多用户将整个相册上传到Facebook,而不是单个照片,这为将相册中的照片批量处理提供了明显的机会。我们在第4节中量化了聚合写入的改进。