论文在: 这里
一个golang开源版本实现: seaweed-FS
毛剑的版本叫bfs
动机
facebook原来的方案是把图片存在NFS上, 由于细小的文件太多, NFS的metadata比较大(which is hundreds of bytes large
), 导致了无法把metadata都cache到内存中, 而且每次要读取一个图片, 至少需要3次磁盘IO, 第一次读取目录元信息, 第二次读取inode, 第三次读取图片
而且对于facebook的图片请求, 存在很多long tail请求, 就是很长时间以来都很冷门的图片.(个人相册并不是热门数据), 这就会导致很多请求都不能命中CDN或者自身的cache, 直接落到了磁盘.
所以它们做了haystack
haystack设计
haystack 减少了metadata的大小, 让所有的metadata都保存在内存中, 这样所有图片读取就只需要一次的磁盘IO
架构
核心组件:Haystack Directory, Haystack cache, Haystack store
Store是由物理卷组成的,容量由物理卷的数量来控制,100个物理卷可提供10TB的存储空间(100G/volumn)。物理卷还会组合成一个逻辑卷,单个逻辑卷中的所有物理卷数据是相同的,冗余。Directory主要保存应用方面的数据信息,例如每张图片存放在哪个逻辑卷上,还有逻辑卷到物理卷的映射以及逻辑卷的剩余空间。Cache相当于一个内部的CDN。
获取图片的过程
Web Server会请求Haystack Directory为该图片生成相应的url, url格式如下:
|
|
CDN部分指定了CDN的地址, CDN可以通过<Logical-volume,Photo>
来找到cached在CDN的图片. 如果找不到, 那么它会通过这个URL中的Cache
来把请求发到Haystack Cache
组件, Cache组件做跟CDN类似的操作, 用<Logical-volume,Photo>
查找图片, 如果找不到, 继续缩短url, 变成Machine-id
, 去访问Store Machine.
当然, 请求也可以直接发到Haystack Cache
, 这个由Haystack Directory来决定(因为URL是其生成的)
上传图片
从 Haystack Directory请求一个 write-enabled的logical volumn, 然后web Server指定一个唯一的id给图片, 然后把它上传到逻辑卷相应的物理卷上去(通常是三个副本)
Haystack Directory
Directory提供四个主要功能:
- 提供Logical volume到physical volumes的映射关系 . Web Server在上传图片的时候利用这个映射关系以及在构建image URLs的时候.
- 负载均衡
- 决定图片请求是发到CDN还是发到Haystack Cache, 用来调整对CDN的依赖
- 那些达到容量限制的logical volumes或者为了易操作性, Directory将其标记为Read-only
Cache
分布式hash表,用图片id作为key定位cache数据。Cache只会在下面两种情况下都吻合的情况下才进行缓存:
- 请求来自用户而不是CDN
- 从可写的Store机器上读取的数据
对于1:CDN和cache其实是一样的。
对于2:为了保护可写Store机器,有两点,其一,通常新数据都会有较大的访问,其二,我们设计的filesystem在只读或者只写的时候会有更好的性能。
Haystack Store
每一个Store Machine管理着多个physical volumes. 你可以把physical volume想象成一个非常大的文件(100GB), 类似这种格式/hay/haystack_<logical volume id>
. Store Machine可以快速地通过logical volume id和相应的file offset访问到图片. 这个认知对于Haystack来说是是里程碑式的: 通过文件名, offset和文件大小来获取指定的图片而不需要磁盘操作.
Haystack Store的文件格式如下:
每个field的意义如下:
cache到store读:
请求数据:logical volume id, key, alternate key, cookie
cache到store写:
上述 + data。数据是append-only的,对于某些修改,比如旋转,允许在原needle上修改,否则其余所有的修改都会以相同的key和alternate key追加一个needle,如果新的needle被追加到别的逻辑卷里,这些信息会被Directory感知,旧卷上的needle永远都不会被访问到了。如果是追加到相同的逻辑卷里, 那么会以file offset来区分, 更大的offset表示这是一个更新的图片
cache到store删除:
设置flags标志。是在compaction的时候回收这些删除的空间, 并且合并一些重复的图片(update导致)
索引
为了加快系统启动速度,store机器对每个卷都维护一个index文件。实际上是内存索引某个时刻的checkpoint。
会引入问题:index文件可能是旧的。比如当delete照片时,是设置flag而没有升级index,index的升级是异步的,这样可以让写请求快速返回。
需要解决两个副作用:needles可能存在但没有对应的index记录,或者index记录没有反映出delete标志。
解决办法:
对于第一个副作用,系统启动时,从卷尾部往头部扫描,补充index。因为没有index的needles肯定是在最后追加的。
对于第二个副作用,延迟处理,也就是说没人读的话就不管它,有人读时再判断并更新index。
文件系统
根据Haystack的设计,对文件系统的需求是:不需要太多内存(例如保存文件的filemeta数据),在大文件里快速的随机seek。Haystack 选用的是XFS,好处:
- blockmaps很小
- 预分配,减少碎片等等。