跳转到: 导航, 搜索

Swift/ideas/small files/implementation

< Swift‎ | ideas‎ | small files

本页的目的是描述提议的实现方案以及一些基准测试。请注意,实现方案和基准测试都处于早期阶段。

代码 (旧): https://review.openstack.org/#/c/436406/ 幻灯片 (旧): https://www.slideshare.net/rledisez/slide-smallfiles 新幻灯片 : https://fr.slideshare.net/AlexandreLecuyer/openstack-swift-lots-of-small-files

问题

Swift 对象至少在文件系统上使用两个 inode。对于具有许多小文件的集群(例如,每个磁盘 1000 万个对象),性能下降很重要,因为目录结构不适合内存。复制/审计操作会触发大量的 IO。超过 40% 的磁盘活动可能是由“listdir”操作引起的。目标是在不进行任何磁盘 IO 的情况下提供 listdir 操作。

概述

将 Swift 对象存储在大型文件中,类似于 haystack 的做法。我们不需要存储在 inode 中的所有信息(所有者、组)。使“inode”尽可能小,以便可以从内存提供 listdir 请求。这些“inode”将存储在一个键值存储中,每个磁盘一个,以方便集群维护。

实现

新的 Python 代码

  • vfile

一个新的“vfile”模块提供了一个类似于常规 Python 文件的接口,但会将“vfile”存储在大型文件中,我们称之为“卷”。一个卷是只追加的,并且专用于给定的 Swift 分区。一个分区可能有多个卷,以允许写入并发。一旦文件被写入卷,其位置(卷索引、偏移量)就会存储在一个键值存储中。

  • kvfile

“kvfile”是 diskfile 的一个副本,经过修改以使用“vfile”而不是常规 POSIX 文件。

  • rpc

“rpc_grpc”模块通过套接字处理与本地 RPC 服务器的通信(注册文件、列出目录)。它调用从 gRPC 生成的代码

现有的 Python 代码

对 replicator / reconstructor / diskfile / utils 的一些小的修改。主要是抽象文件和目录操作 (os.*)。调用 diskfile 实现。

新的 Golang 代码

RPC 服务器作为单独的进程运行,通过套接字访问,使用 gRPC。每个磁盘有一个 RPC 服务器实例。每个 RPC 服务器嵌入一个 leveldb 键值数据库。基本操作是存储和检索有关卷和文件的信息,以及动态地重新创建 Swift 目录结构(目录不存储)。

一致性

对象服务器当前在回复之前发出 fsync(),以确保数据在磁盘上。vfile 模块也会在写入后同步卷,然后再回复客户端。但是,leveldb 键值存储是异步写入的。对键值存储进行同步操作在性能方面代价太高。

如果 kv 未正确关闭,则在重新启动时会触发检查,并扫描卷的末尾,以协调 kv 内容的任何差异。

释放空间

我们依赖于文件系统的“punch hole”支持,它允许我们在文件中释放空间。 https://lwn.net/Articles/415889/

示例

提供 PUT 请求。而不是创建临时文件并重命名它

  • 找到该分区的一个未锁定的卷。如果需要,创建一个卷并在 KV 中注册它。
  • 锁定卷并将对象写入卷的末尾。当 Swift 关闭“文件”时,写入对象头和元数据,然后 fsync() 卷。
  • 在 KV 中注册文件

提供 GET 请求

  • 从 KV 获取对象位置(卷索引、卷中的偏移量)
  • 打开卷文件,移动到偏移量,提供对象。

初步测试结果 (已过时)

我们尚未测试在生产中看到的病态情况。硬件设置:Atom C2750 2.40Ghz 16GB RAM 驱动器:HGST HUS726040ALA610 (4TB)

每个服务器 3 个驱动器,但下面的测试只使用一个驱动器。

从一台机器到单个已打补丁对象服务器的单线程 PUT,以及一个未打补丁的 2.12 服务器

(使用对象服务器 API 直接进行测试,不涉及代理服务器,对象小于 100 字节)

从零到 400 万个对象,在一个磁盘上。

  • 2.12 版本:3360 分钟 (19.8 PUT/s)
  • 已打补丁版本:2540 分钟 (26.2 PUT/s) - Leveldb 中每个对象使用的空间约为 42 字节

从 400 万到 800 万个对象

  • 2.12 版本:3900 分钟 (17 PUT/s)
  • 已打补丁版本:1700 分钟 (39.2 PUT/s) - 更快,可能因为大多数“卷文件”已经创建 (未测量,待确认)

测试结束时磁盘的键值大小为 320MB。

从一台机器到单个已打补丁对象服务器的单线程 GET,以及一个未打补丁的 2.12 服务器。两台服务器都在一个磁盘上有 800 万个对象。

  • 2.12 版本:39 GET/s
  • 已打补丁版本:93 GET/s


并发 PUT 请求,每秒 20 个,持续 10 分钟,使用“热 inode 缓存”

  • 2.12 版本响应时间分布
  Latencies     [mean, 50, 95, 99, max]  641.274117ms, 67.31248ms, 3.526835534s, 4.68917307s, 5.971909s
  100% success
  • 已打补丁版本响应时间分布
  Latencies     [mean, 50, 95, 99, max]  82.581295ms, 50.487793ms, 261.475566ms, 615.565045ms, 1.245540101s
  success 100%


并发 PUT 请求,每秒 20 个,持续 10 分钟,在丢弃 vm 缓存之后

  • 2.12 版本响应时间分布
  Latencies     [mean, 50, 95, 99, max]  29.211369875s, 30.002788029s, 30.003025069s, 31.001143056s, 33.005231569s
  response below 30s: 6,11%
  • 已打补丁版本响应时间分布
  Latencies     [mean, 50, 95, 99, max]  9.290393071s, 8.216053491s, 24.212567799s, 29.46094486s, 30.001358218s
  response below 30s: 99.26%

LOSF v2

我们现在正在查看 14TB 驱动器。LOSF 的主要问题是 CPU 消耗。我们还想做的是优化 HEAD 请求。

新的对象键格式 (已完成)

新格式为:partition / suffix / object hash / filename。旧格式为:object hash / filename。数据库增长约 5%,但现在列出分区和后缀内容要便宜得多。缺点是我们需要支持 relinker:以前的格式不关心 part power

恢复 hashes.pkl (已完成)

因为列出分区和后缀现在更便宜了,__get_hashes() 可以使用与 diskfile.py 相同的代码,我们可以恢复 hashes.pkl

新的卷选择方法

分析表明当前方法占用 CPU 资源密集。停止为分区分配一个卷。创建卷直到达到最小计数,以避免对象服务器进程之间的锁争用,然后使用这些卷直到它们变得太大,然后将其标记为只读。

在数据库中存储元数据

可选地(如果 LOSF 键值存储位于专用 SSD 上),在数据库中存储 Swift 元数据的副本

添加卷统计信息

我们有一个 FUSE 挂载,很好,但获取每个卷的统计信息(打孔的空间量、隔离大小)代价很高。将这些信息存储在数据库中

基准测试!

迫不及待,但目前还没有任何内容;)