注意
本文档适用于 Ceph 开发版本。
清单
简介
控制。如../deduplication.rst
,向 RADOS 添加透明重定向机制将使分层解决方案比 RADOS 目前的“缓存/分层”更强大。
请参阅../deduplication.rst
从高层次来看,每个对象都嵌入了一部分元数据,可以映射对象数据有效负载的子集到(引用计数的)其他池中的对象。object_info_t
本文档旨在详细说明:
This document exists to detail:
文件清单数据结构
用于操作文件清单的 RADOS 操作。
状态和计划
预期使用模型
RBD
对于 RBD,主要目标是让 OSD 内部代理或集群外部代理能够透明地在构成 4MB 的 extent 之间移动部分内容,这些 extent 之间在去重池和热基础池之间。
因此,RBD 操作(包括类操作和快照)必须无论对象的当前状态如何,都具有相同可观察的结果。
此外,分层/去重操作必须与 RBD 操作交错进行,而不会改变结果。
因此,这是我预期的分层代理执行基本操作的草图:
将冷 RBD 数据块降级到慢速池:
读取对象,注意当前 user_version。
在内存中运行 CDC 实现来为对象生成指纹。
使用 CAS 类将每个结果 extent 写入冷池中的对象。
提交操作到基础池:
ASSERT_VER
使用从读取中获取的用户版本,如果对象自读取以来已发生变异,则失败。SET_CHUNK
为每个 extent 提交操作到基础池中的相应对象。EVICT_CHUNK
为每个 extent 释放基础池中的空间。MISSING
.
RBD 用户应该看到降级之前的或降级之后的状态。
注意,在 3 和 4 之间,我们可能会泄漏引用,因此需要定期清理以验证引用计数。
将冷 RBD 数据块提升到快速池。
提交
TIER_PROMOTE
对于克隆,所有上述内容都相同,只是初始读取需要一个LIST_SNAPS
来确定哪些克隆存在,并且PROMOTE
或SET_CHUNK
/EVICT
操作需要在操作中包括cloneid
.
RadosGW
对于读取,RADOS 网关 (RGW) 可以像 RBD 一样操作,依赖于 OSD 中的文件清单机制来隐藏正在去重的对象或存在于基础池中的对象的区别
对于写入,RGW 可以像 RBD 一样操作,但可以选择在写入之前进行指纹识别。在这种情况下,它可以立即将目标对象写入 CAS 池,然后原子性地写入具有相应 chunks 的对象。
状态和未来工作
目前,文件清单数据结构的初始版本以及 IO 路径支持和 rados 控制操作都存在。本节旨在概述下一步工作。
从高层次来看,我们的未来工作计划是:
清理:解决下一节中概述的立即不一致和不足之处。
测试:Rados 严重依赖于 teuthology 失败测试来验证缓存/分层等功能。我们需要相应的测试来支持文件清单操作。
快照:我们希望在 rados 快照系统的级别以下去重克隆的部分。因此,以下 rados 操作需要扩展以正确地在克隆上工作(例如:我们应该能够在克隆上调用
SET_CHUNK
清除基础池中的相应 extent,并正确维护 OSD 元数据)。缓存/分层:最终,我们希望能够弃用现有的缓存/分层实现,但要做到这一点,我们需要确保能够解决相同的用例。
清理
现有实现有一些需要清理的地方:
SET_REDIRECT
: 如果对象不存在,则应创建该对象,否则无法作为重定向原子地创建对象。SET_CHUNK
:看起来会触发一个新的克隆,因为 user_modify 在
do_osd_ops
中被设置。这可能不是理想的,请参阅下文的快照部分,了解如何通常将这些操作与快照混合。至少,SET_CHUNK
可能不应该设置 user_modify。看起来假设对象的相应部分不存在(设置
FLAG_MISSING
但没有检查对象中是否已存在相应的 extent。应始终保持 extent 清洁。看起来如果未分块,则无条件清除文件清单,这可能是不正确的。如果它是
REDIRECT
case CEPH_OSD_OP_SET_CHUNK: if (oi.manifest.is_redirect()) { result = -EINVAL; goto fail; }
TIER_PROMOTE
:SET_REDIRECT
清除对象的内容。0af3d1: 看起来会将它们复制回去,但不会取消重定向或清除引用。这违反了重定向对象在基础池中应为空的约束。特别是,只要重定向被设置,看起来所有操作都将被代理,即使是在提升之后,这会破坏目的。我们确实想要PROMOTE
appears to copy them back in, but does not unset the redirect or clear the reference. This violates the invariant that a redirect object should be empty in the base pool. In particular, as long as the redirect is set, it appears that all operations will be proxied even after the promote defeating the purpose. We do wantPROMOTE
能够原子地用实际对象替换重定向,所以解决方案是在提升结束时清除重定向。对于分块文件清单,看起来在提升之前会刷新。提升通常用于准备对象以进行低延迟读取和写入,因此,唯一的效果应该是将任何
MISSING
extent 读取到基础池中。不应执行刷新。
高层次:
看起来
FLAG_DIRTY
不应用于指向去重 extent 的 extent。将变异的 extent 写回去重池需要写入一个新对象,因为之前的对象不能被修改,就像它还没有被去重一样。因此,我们应始终丢弃引用并删除文件清单指针。目前还没有办法“驱逐”一个对象区域。随着对
SET_CHUNK
的更改,始终保留现有对象区域,我们需要一个EVICT_CHUNK
操作来删除 extent。
测试
我们非常依赖于随机失败测试。因此,我们需要将测试扩展到包括去重/文件清单支持。以下是几个触点:
类似于
qa/suites/rados/thrash/workloads/cache-snaps.yaml
的 Thrasher 测试。当然,该测试测试现有的缓存/分层机制。向该目录添加其他文件,这些文件设置去重池。添加对
ceph_test_rados
(src/test/osd/TestRados*
).RBD 测试的支持。
添加一个测试,该测试在 RBD 工作负载与盲提升/驱逐操作并发运行时运行。
RGW
添加一个测试,该测试在 rgw 工作负载与盲提升/驱逐操作并发运行时运行。
快照
基本上,我们需要能够操作克隆的文件清单状态,因为我们希望能够动态提升、刷新(如果克隆创建时的状态是脏的)以及从克隆中驱逐 extent。
因此,计划是允许每个克隆的object_manifest_t
独立。以下是高层次任务的未完成列表:
修改 op 处理管道以允许
SET_CHUNK
,EVICT_CHUNK
直接在克隆上操作。确保恢复检查对象_manifest 之前尝试使用 clone_range 中的重叠。
ReplicatedBackend::calc_*_subsets
是两个可能需要修改的方法。
请参阅snaps.rst
关于librados
快照系统和 OSD 支持细节的概述。我想指出我们可能想要利用的一个特定数据结构。
dedup-tool 需要更新以使用LIST_SNAPS
作为泄漏检测的一部分来发现克隆。
一个重要的问题是,我们如何处理许多克隆经常在相同的偏移量处引用相同的底层 chunks。特别是,make_writeable
通常会创建一个共享相同object_manifest_t
引用的克隆,除了在该事务中修改的 extent 之外。作为该事务提交的一部分提交的元数据必须映射到相同的引用计数,否则我们必须首先增加底层对象的引用计数(或者冒着引用一个已死亡对象的风险)。因此,我们引入一个简单的约定:连续共享相同偏移量引用的克隆共享相同的引用计数。这意味着,调用make_writeable
的写入可能会减少引用计数,但不会增加它们。这会对删除克隆产生一些影响。考虑以下序列
write foo [0, 1024)
flush foo ->
head: [0, 512) aaa, [512, 1024) bbb
refcount(aaa)=1, refcount(bbb)=1
snapshot 10
write foo [0, 512) ->
head: [512, 1024) bbb
10 : [0, 512) aaa, [512, 1024) bbb
refcount(aaa)=1, refcount(bbb)=1
flush foo ->
head: [0, 512) ccc, [512, 1024) bbb
10 : [0, 512) aaa, [512, 1024) bbb
refcount(aaa)=1, refcount(bbb)=1, refcount(ccc)=1
snapshot 20
write foo [0, 512) (same contents as the original write)
head: [512, 1024) bbb
20 : [0, 512) ccc, [512, 1024) bbb
10 : [0, 512) aaa, [512, 1024) bbb
refcount(aaa)=?, refcount(bbb)=1
flush foo
head: [0, 512) aaa, [512, 1024) bbb
20 : [0, 512) ccc, [512, 1024) bbb
10 : [0, 512) aaa, [512, 1024) bbb
refcount(aaa)=?, refcount(bbb)=1, refcount(ccc)=1
e3b18a: 在结束时应该是多少引用计数?根据我们上面的规则,它应该是aaa
be at the end? By our
above rule, it should be 2
,因为这两个`aaa`
引用不是连续的。但是,考虑删除克隆20
initial:
head: [0, 512) aaa, [512, 1024) bbb
20 : [0, 512) ccc, [512, 1024) bbb
10 : [0, 512) aaa, [512, 1024) bbb
refcount(aaa)=2, refcount(bbb)=1, refcount(ccc)=1
trim 20
head: [0, 512) aaa, [512, 1024) bbb
10 : [0, 512) aaa, [512, 1024) bbb
refcount(aaa)=?, refcount(bbb)=1, refcount(ccc)=0
在这一点上,我们的规则规定refcount(aaa)
是1。这意味着删除20
需要检查两侧克隆持有的引用,然后匹配。
请参阅osd_types.h:object_manifest_t::calc_refs_to_drop_on_removal
用于实现此规则的逻辑。
这看起来很复杂,但它让我们获得了两个宝贵的属性:
make_writeable 引用的引用计数变化不会阻塞增加引用
我们不需要加载
object_manifest_t
来确定如何处理删除每个克隆——只需立即之前和之后的克隆。
所有克隆操作在添加或删除引用时都需要考虑相邻chunk_maps
。
数据结构
每个 RADOS 对象都包含一个object_manifest_t
嵌入其中object_info_t
的远程文件系统osd_types.h
):
struct object_manifest_t {
enum {
TYPE_NONE = 0,
TYPE_REDIRECT = 1,
TYPE_CHUNKED = 2,
};
uint8_t type; // redirect, chunked, ...
hobject_t redirect_target;
std::map<uint64_t, chunk_info_t> chunk_map;
}
The type
枚举反映了对象可能处于的三种状态:
TYPE_NONE
: 普通的 RADOS 对象TYPE_REDIRECT
: 对象有效负载由redirect_target
TYPE_CHUNKED: object payload is distributed among objects with size and offset specified by the ``chunk_map
.chunk_map
指定的单个对象支持chunk_info_t
如下所示,也指定了length
,目标OID, andflags
.
struct chunk_info_t {
typedef enum {
FLAG_DIRTY = 1,
FLAG_MISSING = 2,
FLAG_HAS_REFERENCE = 4,
FLAG_HAS_FINGERPRINT = 8,
} cflag_t;
uint32_t offset;
uint32_t length;
hobject_t oid;
cflag_t flags; // FLAG_*
FLAG_DIRTY
在此时间可以发生,如果写入具有指纹的 extent。这应该更改为丢弃指纹。
请求处理
类似于缓存/分层,初始触点是maybe_handle_manifest_detail
.
对于下面列出的文件清单操作,我们返回NOOP
并继续在do_osd_ops
.
内部进行专用处理。oi.size >
0
指示它存在?),我们代理读取和写入。
对于TYPE_CHUNKED
,如果can_proxy_chunked_read
上的读取(基本上,所有 ops 都是读取object_manifest_t chunk_map
中的 extent),我们将请求代理到那些对象。
RADOS接口
要设置去重,必须配置两个池。一个将作为基础池,另一个将作为块池。基础池需要使用以下选项进行配置。fingerprint_algorithm
option as follows.
ceph osd pool set $BASE_POOL fingerprint_algorithm sha1|sha256|sha512
--yes-i-really-mean-it
创建对象
rados -p base_pool put foo ./foo
rados -p chunk_pool put foo-chunk ./foo-chunk
创建一个文件清单对象
rados -p base_pool set-chunk foo $START_OFFSET $END_OFFSET --target-pool chunk_pool foo-chunk $START_OFFSET --with-reference
操作:
set-redirect
在
base_object
。Cephadm 还支持使用base_pool
和target_object
。Cephadm 还支持使用target_pool
之间设置重定向。target_object
.void set_redirect(const std::string& tgt_obj, const IoCtx& tgt_ioctx, uint64_t tgt_version, int flag = 0); rados -p base_pool set-redirect <base_object> --target-pool <target_pool> <target_object>
返回
ENOENT
链接起来如果对象不存在(TODO:为什么?)EINVAL
如果对象已经是重定向。作为操作的一部分,获取目标引用,如果行为集重置并且客户端在获取引用和记录重定向之间死亡,可能会泄漏引用。
截断对象,清除 omap,并清除 xattrs 作为副作用。
在
do_osd_ops
的顶部,不设置 user_modify。此操作不是用户变异,并且不会触发创建克隆。
有两个
set_redirect
:将所有操作重定向到目标对象(类似于代理)
当
tier_promote
被调用时缓存(此时重定向将被清除)。
set-chunk
Set the
chunk-offset
在source_object
中创建一个链接,将其与target_object
.void set_chunk(uint64_t src_offset, uint64_t src_length, const IoCtx& tgt_ioctx, std::string tgt_oid, uint64_t tgt_offset, int flag = 0); rados -p base_pool set-chunk <source_object> <offset> <length> --target-pool <caspool> <target_object> <target-offset>
返回
ENOENT
链接起来如果对象不存在(TODO:为什么?)EINVAL
如果对象已经是重定向。EINVAL
如果参数缓冲区格式不正确。ENOTSUPP
如果现有映射的 chunks 与新的 chunk 映射重叠。作为操作的一部分,获取目标的引用,如果行为集重置并且客户端在获取引用和记录重定向之间死亡,可能会泄漏引用。
截断对象,清除 omap,并清除 xattrs 作为副作用。
此操作不是用户变异,并且不会触发创建克隆。
TODO:
SET_CHUNK
看起来如果未分块,则无条件清除文件清单。if (!oi.manifest.is_chunked()) { oi.manifest.clear(); }
evict-chunk
从对象中清除 extent,只留下它和
target_object
.void evict_chunk( uint64_t offset, uint64_t length, int flag = 0); rados -p base_pool evict-chunk <offset> <length> <object>
返回
EINVAL
之间的文件清单链接如果 extent 不存在于文件清单中。注意:这还不存在。
tier-promote
提升对象,确保后续读取和写入将是本地
void tier_promote(); rados -p base_pool tier-promote <obj-name>
返回
ENOENT
如果对象不存在对于重定向文件清单,将数据复制到头部。
TODO: 在重定向对象上提升需要清除重定向。
对于分块文件清单,将所有缺失的 extent 读取到基础池中,后续的读取和写入将从基础池提供服务。
实现说明:对于分块文件清单,调用
start_copy
在自身上。结果copy_get
操作将发出读取,然后由正常的文件清单读取机制重定向。不设置
user_modify
标志指示 cephadm 移除主机以及 CRUSH 桶。未来工作将涉及添加支持以指定一个
clone_id
.unset-manifest
在具有文件清单的对象中清除文件清单信息。
void unset_manifest(); rados -p base_pool unset-manifest <obj-name>
清除文件清单 chunks 或重定向。懒惰地释放引用,可能会泄漏。
do_osd_ops
看起来不包括它user_modify=false
ignorelist
,因此将触发快照。注意,即使对于重定向也是如此SET_REDIRECT
也不翻转user_modify
。这应该被修复——unset-manifest
不应该是一个user_modify
.tier-flush
将具有 chunks 的对象刷新到块池。
void tier_flush(); rados -p base_pool tier-flush <obj-name>
包括在
user_modify=false
ignorelist
中,不会触发克隆。不会驱逐 extent。
ceph-dedup-tool
ceph-dedup-tool
有两个功能:找到去重分块的最佳 chunk 偏移量和修复引用计数(见./refcount.rst
).
找到最佳 chunk 偏移量
固定 chunk
要找到固定 chunk 长度,您需要多次运行以下命令,同时更改
chunk_size
.ceph-dedup-tool --op estimate --pool $POOL --chunk-size chunk_size --chunk-algorithm fixed --fingerprint-algorithm sha1|sha256|sha512
Rabin chunk(Rabin-Karp 算法)
Rabin-Karp 是一种基于滚动哈希的字符串搜索算法。但是,滚动哈希不足以进行去重,因为我们不知道 chunk 边界。因此,我们需要使用滚动哈希进行基于内容的切片,以进行内容定义的 chunking。
希望使用去重的用户需要找到理想的 chunk 偏移量。要找到理想的 chunk 偏移量,用户应该通过
ceph-dedup-tool
发现其数据工作负载的最佳配置。这些信息将用于通过set-chunk
API 为集群设置默认值。ceph-dedup-tool --op estimate --pool $POOL --min-chunk min_size --chunk-algorithm rabin --fingerprint-algorithm rabin
ceph-dedup-tool
对象分块。rabin chunk
。这些是rabin chunk
.--mod-prime <uint64_t> --rabin-prime <uint64_t> --pow <uint64_t> --chunk-mask-bit <uint32_t> --window-size <uint32_t> --min-chunk <uint32_t> --max-chunk <uint64_t> Users need to refer following equation to use above options for ``rabin chunk``. :: rabin_hash = (rabin_hash * rabin_prime + new_byte - old_byte * pow) % (mod_prime)
固定 chunk 与内容定义 chunk 的选项
内容定义 chunking 可能是或可能不是最佳解决方案。例如,
数据 chunk
A
:abcdefgabcdefgabcdefg
让我们考虑 Data chunk
A
的去重。理想的 chunk 偏移量是从1
to7
(abcdefg
)。所以,如果我们使用固定 chunk,7
是最佳 chunk 长度。但是,在内容切片的情况下,最佳 chunk 长度可能无法找到(去重率不会是 100%)。数据 chunk
B
:abcdefgabcdefgabcdefg
数据 chunk
C
:Tabcdefgabcdefgabcdefg
修复引用计数
引用计数去重的关键思想是误报,这意味着
(manifest object (no ref),, chunk object(has ref))
发生而不是(manifest object (has ref), chunk 1(no ref))
。为了修复这种不一致,ceph-dedup-tool
支持chunk_scrub
.ceph-dedup-tool --op chunk_scrub --chunk_pool $CHUNK_POOL
由 Ceph 基金会带给您
Ceph 文档是一个社区资源,由非盈利的 Ceph 基金会资助和托管Ceph Foundation. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.