注意

本文档适用于 Ceph 的开发版本。

快照

概述

Rados 支持两种相关的快照机制:

  1. 池快照(pool snaps):快照隐式应用于池中的所有对象

  2. 自管理快照(self managed snaps):用户必须在每次写入时提供当前的 SnapContext

这两种机制是互斥的,一个特定的池只能使用其中一种。

SnapContext 是为对象当前定义的一组快照,以及为排序目的从 mon 请求的最新快照(seq)(具有较新的 seqSnapContext 被认为是更新的)。

从 OSD 的角度来看,池快照自管理快照 之间的区别在于 SnapContext 是通过客户端的 MOSDOp 到达 OSD,还是通过最新的 OSDMap 到达 OSD。

有关详细信息,请参阅 manifest.rst

磁盘上的结构

PG 集合中的每个对象都有一个 head 对象,并且可能有一组 clone 对象。每个 hobject_t 都有一个 snap 字段。对于 head(对象唯一可写的版本),snap 字段设置为 CEPH_NOSNAP。对于 clones,snap 字段设置为创建时 SnapContextseq。当 OSD 处理写入时,它首先检查最新的 clone 是否使用早于 SnapContext 中表示的最新快照的 snapid 标记。如果是,则在写入时间和上次克隆时间之间至少发生了一次快照。因此,在执行突变之前,OSD 会创建一个新的克隆,用于处理上次克隆的 snapid 和最新 snapid 之间的快照读取。

head 对象包含编码在属性中的 SnapSet,用于跟踪:

  1. 为对象定义的快照的完整集合

  2. 当前存在的所有克隆的完整集合

  3. 用于跟踪空间使用的克隆之间的重叠区间

  4. 克隆大小

当仍有克隆存在时,head 无法被删除。相反,它被标记为白化(object_info_t::FLAG_WHITEOUT)以容纳其中包含的 SnapSet。在这种情况下,head 对象在逻辑上不再存在。

参阅:should_whiteout()

此外,每个克隆上的 object_info_t 都包含一个向量,其中包含定义该克隆的快照。

快照移除

要删除快照,需要向 Monitor 集群发出请求,将快照 ID 添加到已清除快照列表(或者在 池快照 的情况下将其从池快照集中删除)。无论哪种情况,PG 都会将快照添加到其 snap_trimq 以进行修剪。

当其所有快照都被移除时,克隆可以被移除。为了确定在移除快照时可能需要移除哪些克隆,我们使用 SnapMapper 维护从快照到 hobject_t 的映射。

参阅 PrimaryLogPG::SnapTrimmer, SnapMapper

当 PG 清洁且未进行擦洗时,此修剪由 snap_trim_wq 异步执行。

  1. 选择 PG::snap_trimq 中的下一个快照进行修剪

  2. 我们从 PG::snap_mapper 中确定下一个要修剪的对象。对于每个对象,我们创建一个日志条目并 repop 更新对象信息和快照集(包括调整重叠)。如果对象是不再属于任何活动快照的克隆,则在此处将其移除。(参阅 PrimaryLogPG::trim_object(),当 new_snaps 为空时。)

  3. 我们还使用对象的新快照本地更新我们的 SnapMapper 实例。

  4. 包含对象修改的日志条目还包含新的快照集,副本使用该快照集更新其自己的 SnapMapper 实例。

  5. 主节点与副本共享信息,副本将新的 purged_snaps 集与其余信息一起持久化。

恢复

因为修剪操作是使用 repops 和日志条目实现的,所以正常的 PG 对等和恢复会维护快照修剪器操作,但需要注意的是,推送和移除操作需要更新本地 SnapMapper 实例。如果 purged_snaps 更新丢失,我们只会重新修剪一个现在为空的快照。

SnapMapper

SnapMapper 是在 map_cacher<string, bufferlist> 之上实现的,后者通过文件系统等后端存储提供接口,支持异步事务。当事务未完成时,map_cacher 实例会缓冲不稳定的键,从而实现一致访问,而无需刷新 filestore。SnapMapper 提供两个映射:

  1. hobject_t -> set<snapid_t>:存储每个克隆对象的快照集

  2. snapid_t -> hobject_t:存储具有该快照作为其快照之一的 hobjects 集

假设:有许多 hobjects 和相对较少的快照。第一个编码将对象的字符串化作为键,将快照集的编码作为值。第二个映射,因为一个快照可能有许多 hobjects,被存储为形式为 stringify(snap)_stringify(object) 的键集合,其中 stringify(snap) 是固定长度。这些键具有 bufferlist 编码对<snapid, hobject_t> 作为值。因此,创建或修剪单个对象不涉及读取任何快照的所有对象。此外,在构造时,SnapMapper 被提供一个掩码,用于过滤属于该 PG 的单个 SnapMapper 键空间中的对象。

拆分

snapid_t -> hobject_t 键条目被安排成这样:对于任何 PG,最多需要检查 8 个前缀才能确定特定 PG 中特定快照中的所有 hobjects。拆分时,父节点上要检查的前缀会进行调整,以便只显示保留在 PG 中的对象。子节点将立即拥有正确的映射。

clone_overlap

附加到 head 对象的每个 SnapSet 都包含克隆对象之间的重叠区间,以优化空间。重叠区间存储在 clone_overlap 映射中,映射中的每个元素都存储快照 ID 和与下一个最新克隆的相应重叠。

参阅以下使用 4 字节对象的示例

object

内容

head

[AAAA]

listsnaps 输出如下

cloneid

snaps

大小

overlap

head

4

在拍摄快照(ID 1)并重新写入对象的前 2 个字节后,创建的克隆将在其最后 2 个字节与新的 head 对象重叠。

object

内容

head

[BBAA]

克隆 ID 1

[AAAA]

cloneid

snaps

大小

overlap

1

1

4

[2~2]

head

4

通过拍摄另一个快照(ID 2),这次只重新写入对象的前 1 个字节,创建的克隆(ID 2)将在其最后 3 个字节与新的 head 对象重叠。而最旧的克隆(ID 1)将在其最后 2 个字节与最新的克隆重叠。

object

内容

head

[CBAA]

克隆 ID 2

[BBAA]

克隆 ID 1

[AAAA]

cloneid

snaps

大小

overlap

1

1

4

[2~2]

2

2

4

[1~3]

head

4

如果通过重新写入 4 个字节完全重新写入 head 对象,唯一保留的重叠将是两个克隆之间的重叠。

object

内容

head

[DDDD]

克隆 ID 2

[BBAA]

克隆 ID 1

[AAAA]

cloneid

snaps

大小

overlap

1

1

4

[2~2]

2

2

4

head

4

最后,在移除最后一个快照(ID 2)并且 snaptrim 启动后,将不再有重叠区间

object

内容

head

[DDDD]

克隆 ID 1

[AAAA]

cloneid

snaps

大小

overlap

1

1

4

head

4

由 Ceph 基金会为您呈现

Ceph 文档是由非营利性 Ceph 基金会 资助和托管的社区资源。如果您希望支持这项工作和我们的其他努力,请考虑 立即加入