摘要
这篇文章描述了 针对facebook 图片应用优化的存储系统 Haystack。FB目前存储超过 2600 亿图片,超过20PB。用户每周上传10亿张新图片(60T),FB 峰值每秒处理100万图片。相对于之前的NFS方案,Haystack 提供了更加便宜并且高校的解决方案。我们观测到NFS方案主要消耗了太多的磁盘IO在元数据查找上。Haystack减小了元数据,所以Haystack可以在内存中查找所有的元数据,这个方案减小了磁盘操作,因此提高了吞吐量
简明扼要的指明了 高效的原因在与磁盘IO少,手段是通过减小元数据,这样可以用内存存储所有的元数据。可能很多新手朋友不懂元数据什么意思,其实主要就是文件系统的 inode 表。可以google看看
1.介绍
分享图片是FB最热门的特性。迄今为止,用户已经上传了超过650亿张图片,这让FB成为了全球最大图片分享网站。每一张图片上传,FB会会存储4张不同尺寸的照片,因此存储了超过2600亿张图片,超过20PB。用户每周上传10亿张新图片(60T),FB 峰值每秒处理100万图片。预期今后还会继续增长,因此图片存储系统对FB基础设施来说是一个巨大的挑战。
这篇论文介绍了Haystack的设计和实现,FB的图片存储系统已经在生产环境上运行了2年了。Haystack是我们针对 FB 上 图片分享而设计的,这些数据 一次写入,频繁读取,从不修改,几乎不删除。因为传统方案在 FB 这种负载下性能较差,所以我们自己设计了Haystack。
根据我们的经验,我们发现 POSIX 文件系统的不足之处在与 目录 和 每个文件的元数据。对图片应用来说,例如权限这种元数据是没用的而且浪费存储容量。而且更浪费的地方在于这些元数据需要从磁盘读入内存,排序,这样才能找到文件本身。在小系统上,这样的浪费是微不足道的,对于百亿张,PB级别的数据,获取这些无用的元数据就成了系统吞吐的瓶颈。我们发现这是我们的主要问题在之前使用的 NAS+NFS系统。读一张图片需要几个磁盘操作:1(或者更多)个用于 将filename转换为 inode,另一个用于读入inode信息,最后一个读文件本身。说白了,在频繁读取的情况下,读取元数据的磁盘操作成为了性能瓶颈。 因为读取元数据这样的额外性能花费,我们必须依赖 CDN,例如 Akamai,来应对频繁读取
解释一下元数据,比如 我们查找 a.png 文件,我们即使知道 a.png 的路径,我们也是无法直接找到他的,首先要先查找 inode (保存了所有文件的 inode号等信息)表(一次IO),找到 inode 号,根据这个inode号找到inode信息(保存了文件位置等信息)(一次IO),在找到文件(一次IO),当文件非常非常非常多的时候,inode表,无法一次全部装在内存,就需要多次IO了
指出NAS+NFS方案的不足之处后,Haystack在设计上主要达到如下4个主要指标
高吞吐低延迟:Haystack 必须能抗住用户的访问,超出系统处理能力的请求,一种情况是被忽略(对于用户体验来说是不可接受的),一种是用CDN处理(价格贵,超过某个值后回报率会开始减小),为了用户体验好请求应该被快速响应。Haystack 一次请求至多一次磁盘操作,Haystack 是通过尽量减少元数据,进而将所有元数据放在内存中 ,这样就达到了高吞吐低延迟的目标。
容错:在大规模系统中,故障每天都发生。无论是系统crash,还是硬件故障,用户都必须可以正常获取服务。即使某个数据中心停电了 或者 海底光缆断开,服务也必须是可以正常访问的。Haystack 在异地进行备份图片,一台机器出问题了,另外一台顶上来,必要时复制冗余数据
高性价比:相对于我们之前的NAS+NFS方案,Haystack性能好,价格低廉。通过两个维度来衡量节约的成本:每TB存储花费,正常存储器读取速度。相对于NAS+NFS方案,存储花费减少了28%, 读取速率是NAS+NFS 的4倍多。
简单:在生产环境中我们不能过分的强调设计上的优秀,应该是着眼于便于实现与维护。Haystack 是一个新的系统,缺乏在生产环境中长达几年的测试。我们特别关注简单。因为简单,所以我们在几个月中构建部署了一个可以工作的系统而不是几年。
这篇论文描述了一个 每天处理数十亿张图片的生产系统 的从思想到实现上的经验,Haystack 三个主要贡献:
- 高效的对象存储系统,检索数十亿张图片
- 构建,扩展一个可靠,可用,廉价的图片存储系统上的教训
- FB 图片分享应用的特征
剩余部分如下:章节2描述了采用NAS+NFS 架构的背景和面临的挑战。章节3描述了 Haystack 的设计和实现。章节4描述了FB 图片的读写负载,表明了 Haystack达到了设计目标,章节5是一些对比相关的工作,章节6总结
2.背景&NAS+NFS架构
在这个章节,描述了 NAS+NFS 架构,突出了主要教训。受限于论文篇幅,我们会省略一些NAS+NFS的细节。
2.1 背景
以 NAS+NFS 典型设计开头, 在一个热门的站点上,CDN,存储系统 如何工作的。图1,描述了一个用户从访问web ,到最终下载图片到本地磁盘的整个流程。当访问一个页面是,第一步浏览器会发送一个request 到 web server 获取一个URL,这个URL 描述了去哪里下载这张图片,对于热门的系统,这个URL 通常指向CDN,如果CDN缓存了这张图片就直接返回给用户,如果没有缓存CDN会根据URL中的信息去存储系统中检索这张图片,CDN缓存这张图片后在发送给浏览器
FB最初的设计使用了 NFS 这种方案,这个小节剩余部分会介绍一些这种方案的细节。我们吸取到的主要教训是,CDN没有为一个热门社交网站提供一个实际的解决方案。CDN对于热门照片非常有效,比如最近上传的照片,个人照片。但是对于热门社交网站来说,会有大量的非热点图片的请求,我们将这种情况称为 long tail。long tail 占据了很大一部分流量,几乎所有进入到后端图片存储系统都是这种请求,因为long tail 在CDN中几乎不会命中缓存。如果将所有的图片都缓存下来效果会非常好,但问题是这样做内存的消耗太大了。
基于NFS的设计将每张图片存储在商业NAS上。物理机, photo server, 挂载所有NAS上的导出的卷通过NFS网络。图2描述了 Photo Store server 如何处理 HTTP 图片请求的过程。通过 图片的URL可以提取出 设备卷号和文件全路径,进而通过NFS网络获取到图片,在返回给CDN。
我们最初每个文件夹存储成千上万个文件,这种做法导致了 即使只访问一张图片也要进行好几次磁盘操作。因为NAS系统管理文件夹元数据的机制,将成千上万个文件放在一个文件夹下是极其低效的,因为目录的 blockmap 相对于 缓存容量来说是太大了。因此检索一张图片,花费超过10次的磁盘操作很正常,每个文件夹下只放几百张照片,也至少要花费3次磁盘操作,1.读取文件夹的元数据 2.读取文件inode信息 3.读取文件本身。
为了进一步减少磁盘操作,我们显示的缓存 file handle。第一次读取文件时缓存 file handle 到 memercache。 我们为 os 内核添加了一个 open by filehandle,因此当一个请求命中缓存的时候,可以直接通过缓存下来的 file handle 打开文件。遗憾的是,这种方式对于非热点图片提升不大。一个可以讨论的可行性方法是缓存下来所有的file hanlde,然而这个办法还是需要缓存下来所有的 文件inode信息,代价太大了。我们得到的主要教训是,如果仅仅针对缓存,无论是NAS自身缓存,还是 类似membercache这种外部缓存,对于减小磁盘操作都是有限的。系统终究是要处理 long tail 和 非热门图片的问题,这些请求都是CDN 和 我们系统没有命中缓存 的请求
讲了这么多, 其实核心问题是如果都缓存下来需要的内存多,对于FB这种海量数据是无法接受的,只缓存热点数据内存可以接收了,但是对于非热点数据的请求无法有效处理。假设内存无数,这个问题就不是问题了,所以读者要搞清楚研究的问题是什么。
2.3 讨论
很难提供一个何时需要自建一个存储系统的清晰的指导,但是大家可以为什么 FB 决定构建 Haystack系统中得到启发。
面对NFS方案的瓶颈,我们探讨了是否可以构建一个类似GFS的系统。由于我们的大部分用户数据存储在Mysql上,所以我们主要的场景是针对 工程师工作文件,日志文件,图片文件。对于工作文件,日志文件, NAS有着非常好的性价比, 此外,我们利用Hadoop处理海量日志文件。但是在关于 非热门图片的处理上,无论是Mysql,NAS, 还是Hadoop都没有提供很好的支持。
我们面临的困境是,现存的系统都缺乏正确的 RAM-to-disk 比率。然后,并不存在一个绝对正确的比率。系统只需要有足够的内存,那么所有的元数据就可以被一次缓存起来。在NAS方案中一个图片就是一个文件,至少需要一个inode,一个inode有几百byte,提供足够大的内存并不划算。为了获得一个更好的性价比,我们决定构建Haystack,减少每个图片的元数据,这样需要的内存就少了,相对于NAS来说更划算。
3.设计和实现
FB 使用CDN来支撑热门图片查询,使用 Haystack 解决 long tail 访问效率问题。站点遇到 静态内容的IO瓶颈时传统方案就是使用CDN,CDN承担了大部分请求,后端server只需要处理一小部分请求就可以了。在FB,为了不受限于IO,CDN缓存了难以置信的海量内容。
需要知道的是,在不久的将来CDN 将无法完全解决我们面临的问题,所以我们开发了Haystack用来解决这个严重的瓶颈--磁盘操作。对于非热门图片请求,我们旨在减少磁盘操作,只进行必要的操作。Haystack 通过减少文件系统的元数据,使所有的元数据都可以放在内存中,达到减少磁盘操作的目的的。
回想一下,一个图片存储到一个文件中的做法导致了过多的元数据,因此难以被缓存。Haystack 采用了一个直接的办法:多个图片保存在一个文件里,维护一个大文件。我们会证明这个直接的方法会非同寻常的有效。此外,我们认为设计的简洁性对于快速实施部署是非常重要的。我们现在开始讨论如何使用这个核心技术和周边组件构建一个可靠的,高可用的系统。在接下来的描述中,我们需要区分两种元数据,1. 应用元数据,构造访问URL需要的信息。2. 文件系统元数据, 检索磁盘上图片需要的信息。
3.1 概述
Haystack 包含3个核心组件:1. Haystack Store 2. Haystack Directory 3.Haystack Cache。 为了描述方便我们省略Haystack。Store 负责持久化图片并且管理文件系统元数据。Store存储数据在物理卷上。例如,一个server提供10TB数据存储,那么可以有100个卷,每个卷提供10GB的存储。更进一步,将不同机器上的物理卷对应一个逻辑卷。在Haystack一个逻辑卷上存储一张照片,这张照片会被存储到所有对应的物理卷上,这个冗余可以避免由于磁盘故障,磁盘控制器bug等导致的数据丢失。Directory 维护了物理卷到逻辑卷的映射关系以及其他一些应用元数据,例如 图片在哪个逻辑卷,逻辑卷的剩余空间。Cache作为我们内部的CDN,作用是 在上游CDN没有命中或者需要重新拉取数据的时候 为 Store 抵挡住请求。
图3 描述了Store,Directory, Cache 如何与 用户浏览器,web server, CDN 是如何协作的。在 Haystack 架构中,浏览器可以直接访问CDN或者Cache。需要注意的是 Cache 本质上就是CDN,只是为了区分,我们将外部那个称为CDN,内部这个称为Cache。有了Cache,我们减少了对CDN的依赖。当用户访问一张图片,web server 使用 Directory 构造一个URL。URL 包含了一些信息,包含了访问一个图片的步骤,典型的URL如下:
http://⟨CDN⟩/⟨Cache⟩/⟨Machine id⟩/⟨Logical volume, Photo⟩
第一部分是访问哪个CDN,CDN根据URL最后部分的信息(逻辑卷,图片ID)就可以查询到图片。如果CDN未命中缓存,删除URL CDN信息,访问Cache, Cache中查询与CDN中查询类似,如果Cache未命中,就根据URL中信息去访问Machine获取图片。如果请求直接去Cache访问情况类似,只是去掉URL中CDN的信息。
图4 描述了Haystack的上传文件流程,当用户上传图片时,第一步发送数据给 web server,接下来,请求从Directory 获取一个可写的逻辑卷,最后web server 给图片分配一个唯一ID,将图片上传到逻辑卷对应的所有物理卷上。
3.2 Haystack Directory
Directory 服务提供4个主要功能。1. 提供了逻辑卷到物理卷的映射关系,供用户在上传图片,构造URL时候使用。2.在读写时做负载均衡。 3. 判定是需要从CDN读取还是直接从Cache读取,这个功能可以调整对CDN的依赖。 4. 判定哪些卷是只读的,卷被设定为只读有可能是因为运维需要,也有可能是没有空间在进行写了。为了操作方便我们以机器为粒度来划分只读。
当通过添加新机器的方式来增加容量时,那些机器是可写的;只有可写的机器会收到upload请求。随着时间流逝,这些机器的可用容量会减小。当一个机器没有可用容量时,这个机器就被标记为只读的。下一小节,我们讨论可读功能是如何影响Cache 和CDN的
Directory 是一个相对简单的组件,直接存储信息在一个可以通过PHP接口访问有备份的数据库,可以将数据库替换为memcache以便降低延迟。某个机器如果出现了数据丢失,这个机器的在映射(逻辑卷到物理卷的映射)中将会被移除,有新机器加入后替换掉出错的机器。
3.3 Haystack Cache
Cache 会接受来自CDN或者用户浏览器的请求。Cache 是一个分布式的哈希表,用图片ID做哈希。如果Cache不能直接相应请求,Cache将会从URL中指定的机器上拉取数据,取到数据后在返回给CDN或者用户浏览器。
现在我们强调Cache一个重要的缓存策略。Cache 只在两种情况下对图片进行缓存:1.请求直接来自用户浏览器 2.从可写的机器上拉去下来的图片。解释一下为什么要有这两种策略,策略1:因为如果CDN未命中,Cache也没必要命中(译者解释:不是很明白,可能认为走CDN的交给CDN,CDN不命中的说明是非热点数据,对于非热点数据不缓存,直接走浏览器的可能是认为都是热点数据,或者说无法区分是否为热点认为是热点)。 策略2:图片上传以后通常会有比较大的访问量,系统只读或者只写性能比较好,但是读写混合性能不好。对于可写的机器来说如果不进行缓存会有大量的读请求进来。鉴于这个特点,我们计划做一个主动推送最近上传图片到Cache的优化,因为最近上传的图片会很快,很频繁的被读取。
3.4 Haystack Store
Store的接口很基本,读数据只需要一个带有图片ID, 确定逻辑卷,确定的物理机的请求,如果这台机器找到了就将图片返回回去,找不到就返回错误。
每台机器管理多个物理卷。每个物理卷保存几百万的图片。具体来说,读者可以将物理卷想想为一个巨大的文件保存为‘/hay/haystack <logical volume id>’。只需要提供逻辑卷ID和文件offset,就可以很快的找到文件