Swift/ideas/小文件
Swift 中的小文件优化
影响: Haystack, bluestore, git 包文件
将每个文件作为单独的文件存储的一个大问题是,这会在驱动器上创建大量的 inode。 如果你的集群中有小对象(很常见),并且有大容量驱动器(越来越常见),那么仅 XFS 分区的 inode 和目录项就可能耗尽你的 RAM。 Swift 尝试将这些内容保存在页面缓存中,但它实在太大了。 这意味着
- 存储存在大量的 FS 元数据开销
- 任何需要遍历每个文件的操作都 *很慢*
- 小的纠删码对象在考虑 FS 开销时最终可能会变得相对巨大
想法
在每个后缀目录(或分区目录?)中保留两个 FS 树。 一个是“正常”的,即当前的方式。 另一个用于小文件,并使用 slab 文件和索引系统。 slab 文件是磁盘上的一个文件,它是小对象的连接数据+元数据。 索引文件通过名称或哈希以及在 slab 中的偏移量引用 slab 中的每个对象。
挑战
- 碎片或压缩
- 分块传输编码(在不知道 content-length 的情况下)
- 对象服务器可以缓冲例如 1MB(或“小”的任何大小),如果它在第一次读取中,则使用 slab。 否则,使用正常的 FS 文件
- 额外的磁盘寻道以查找 slab 或平面文件
- 在 slab 文件中找到正确的位置
- 仅附加到 slab 文件,并使用压缩过程来处理已删除数据的“空洞”
- 在复制期间协调不同的 slab
- 我们将索引文件保存在 RAM 中,还是按需读取? 如果是前者,这比 inode/dentries 更昂贵还是更便宜? 如果是后者,额外的 IO 的惩罚有多大?
意想不到的额外好处 (?)
- 全局复制可能更快(复制一个 slab 文件而不是许多小文件)
- 更快地引入新驱动器
- EC 中的小文件优化
替代方案
为什么要使用 slab 分配器? 其他东西会更好吗? 也许诀窍(或有趣的部分)仅仅在于 slab 中内容的簿记(环形缓冲区、LSM 树(或 trie)、跳表等)。 实际的媒体数据所在的位置重要吗(旋转驱动器与闪存)? 我怀疑有编写内存分配器经验的人可能会提出有趣的想法。 我们应该使用一个或多个更大的文件在文件系统上吗? 为什么不直接与块设备通信? 在什么时候我们需要自己发明一个完整的文件系统,并且在这种情况下,我们与已经可用的文件系统相比有什么好处?
链接
值得阅读/查看
- https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf
- https://github.com/chrislusf/seaweedfs (Haystack 实现)
- http://www.ssrc.ucsc.edu/Papers/wang-mss04b.pdf
想进一步讨论? 在 IRC 上找到 notmyname