从《Finding a needle in Haystack: Facebook’s photo storage》大致对比着翻译看了几遍,有些理解和想法,便记录下来。

Haystack和TFS都是面向对象的分布式存储系统,最主要的需求都是为了解决PB级别的图片存储问题,文中也提到了一些设计目标,但设计目标是问题驱动的,先看facebook前期存储模型:

older_haystack

它们存在的问题:硬盘存储慢,访问效率低;CDN速度快,价格高,图片容量大。

设计目标就简单了:1.高吞吐、低延迟;2.高性价比;存储的要求:3.容错;最后就是软件工程的要求:4.设计简单。

这些设计目标里面,第二点是排首位的,实现方法只能是使用便宜硬件,通过软件能力增强硬件能力;第一点就要压榨现有软件数据流程,分析清楚阻塞点在何处,对症下药;三四点都是对便宜硬件能力的软件补充。其中压榨现有的数据流程才是关键。

看一下具体的基于NFS的设计架构

oenhan

浏览器其他数据简略为与网络服务器交互,图片则直接与CDN交互,CDN获取不到的就需要通过图片存储服务器解析到具体的NAS存储上。

具体流程一层层的看,从图片存储服务器上,通过NFS获取图片,互联网上的海量图片存储到通用文件系统,单个目录下可能不止万计的文件,查找起来效率之低,(主要是目录项越大,每次读取磁盘的次数越多),按每次读取一个4k块算,ext3_dir_entry_2目录项平均也有120b,每个目录最多30多个文件,查找文件的访问硬盘次数就会增加。即使按照最优结果,获取一个图片内容仍需要访问3次硬盘:读取目录的元数据、然后获取图片inode信息、读取文件内容。

还有另外一个问题就是新增加的文件,为了防止多个名称重复,几万个文件要依次比较一遍,即使现在有了hash也不给力。

提高访问速度,facebook使用CDN缓存热点图片,使用了memcache缓存长尾图片的目录项和inode,减少硬盘访问。但是目录项和inode乘上亿万级的数量,内存也不够用了。

前面说的一堆困难,就是当前的通用文件系统生产能力不能满足互联网公司日益增长的图片存储需求。

只能再写一个文件系统,完全重写肯定开发和维护成本剧增,那就在外面加一层壳,简化访问流程,就是Haystack。

haystack具体设计要点,CDN缓存热点图片,应该有优秀的算法控制热点的变更,淘宝好像是通过tfs控制热点的替换,facebook没有提到。非热点的存储占了总体积的绝大部分,存储到普通机械硬盘上,相关的元数据缓存到内存里面,降低硬盘访问次数。解决单个小文件元数据相对太大的问题,就把多个小文件通过自有机制压缩成大文件,每次读取大文件的一部分(可能包含多个小文件),类似通用文件系统中的预读功能,大文件的内部读取策略也要控制一下,本质上是将一个线性结构改造成树状结构

再来张图

oenhan

haystack改造成如上,先说容错处理,插一句名言,硬盘只有两种状态,一种已坏的,一种是马上就坏的。事实上服务器上的硬盘非常不稳定,需要用多个物理卷对应一个逻辑卷进行处理,用廉价的空间换高可靠性,通过冗余就可以避免磁盘坏块或者磁盘控制器bug等错误导致的数据丢失。

haystack由三部分组成,Haytack Store、Haystack Directory和Haystack Cache每个和具体的文件系统机制是一一对应的,Store对应的就是通用文件系统,硬盘存储,容量大速度慢;Directory则是目录高速缓存,将目录下的元数据(文件名,inode)放入内存;Cache对应的文件系统机制则是file Cache,缓存具体文件数据。

分布式系统中文件的存储是用过URL唯一标识的,类似于http://<CDN>/<Cache>/<Machine id>/<Logical volume, Photo>,CDN指明了首先进入查找的模块地址,根据<Machine id>/<Logical volume, Photo>快速匹配到具体的文件,如果没找到则进入Cache,还没找到则准备进入<Machine id>/<Logical volume, Photo>。

具体模块的功能:
Haystack Directory:提供了Logical volume到具体物理存储的映射,根据映射控制物理存储设备之间的负责均衡,那些物理存储设备可写等操作,物理设备替换,CDN是否使用等。事实上,Directory是整个系统的控制枢纽,所有数据流都要经过这里,对应的数据采集、挖掘、优化,都要源于此处。

Haystack Cache:可以理解成一个分布式的Hash Table,使用图片ID定位内存中数据的位置。如果Cache没有命中,则从Store中获取文件,然后根据算法反馈到Cache里面(是否保持到Cache)。原文中提到一个有意思的判断条件,图片请求直接来自浏览器而非CDN,且请求的store是可写的,就要缓存到Cache里面,前一点说明了这是非热门图片,后一点则根据读写分离(读干扰写速度)的要求不让在物理写设备上产生大量的读操作,对应的用户习惯是大部分图片在上传之后很快会被频繁访问(SNS效果更明显,电商可能就不太明显 了),然后逐步走long tail。应该上次之后就保留到Cache或者CDN中,这个应该存在算法判断单个图片根据时间递减的热度保存到CDN中,但按Haystack描述的系统会更简单吧。

Haystack Store:Store设计的一个重要原则就是:不需要磁盘操作就可以检索文件名、偏移量、文件大小等元数据,一次磁盘读操作就可以完成图片的读取,Store会将所有的文件描述符(file_struct)和文件元数据(文件、大小等)缓存到Directory中。

下面描述一下物理卷和内存映射之间的结构,一个物理卷可以理解成一个大文件(100GB),前面已经提到用来减小通用文件系统的元数据,其中包含了代表一个图片的needle结构体:

needle

needle_fields

具体操作流程:

1.读取图片:当Cache向Store发送读请求的时候,Cache需要提供逻辑卷ID、key、alternate key,和cookie等,cookie相当于Cache发送给store的密钥,只有cookie匹配的时候,才能从Store中获取图片。当Store接受到读请求后,通过内存缓存读取相关元数据,在卷文件中快速seek到对应的offset,读取整个needle,然后检查cookie是否一致,以及checksum文件完整性,如果全部合法则返回文件给Cache。

2.写入图片:上次文件到haystack,web服务器向Directory查询获取可写逻辑卷和对应的store机器,向Store发送id、key、alternate key、cookie和真实数据,对应每个store上为图片创建needle,更新内存映射。Haystack并不直接支持图片修改操作,修改操作相对于重新上传了一个新文件到store,只不过他们的key和alternate key是相同的。

3.删除图片:将内存缓存中的flag标识设置文件已删除状态(软删除),当Store接收到已删除文件的操作时会检查flag并返回错误信息。needle真正删除文件是通过压缩操作完成的。

4.图片索引:此处的索引不是帮助文件读取的,而是帮助快速构建图片缓存(内存映射)的,可以直接理解为内存映射的存档。由于索引是异步更新的,那么就会:一个needle可能没有对应的索引记录、索引记录中无法得知图片已删除。我们将没有索引的needle成为orphan needle,类似orphan inode概念,系统重启动时候依次为每个orphan needle重新创建匹配的索引记录;由于索引记录无法得知图片被删除,可以在Store机器读取整个needle后检查其flag,若标记为已删除,则更新内存中映射的flag,并回复Cache此对象未找到。

通用文件系统:Haystack使用xfs作为卷文件的存储文件系统,而tfs则采用的是ext4,对于选择上的不同,技术选型和存储人才对此有较大影响。

 

待续


Haystack与TFS:对分布式文件存储系统的理解来自于OenHan

链接为:https://oenhan.com/haystack-tfs

发表回复