YAOTU INSIGHTS

Ceph Crimson SeaStore 逻辑地址(laddr)设计解析:从 64 位 Hint 到 128 位静态布局

Ceph Crimson SeaStore 逻辑地址(laddr)设计解析:从 64 位 Hint 到 128 位静态布局
Ceph Crimson SeaStore 逻辑地址laddr设计解析从 64 位 Hint 到 128 位静态布局【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址: https://gitcode.com/gh_mirrors/ce/ceph本文以 Ceph 仓库中的设计文档 doc/dev/crimson/seastore_laddr.rst 为骨架结合 src/crimson/os/seastore/seastore_types.h 等源码实现深入解析 SeaStore 逻辑地址laddr的位布局设计它为何放弃传统 64 位 hint、如何用 128 位静态布局在逻辑地址中编码对象语义信息以及该设计如何加速克隆、支持范围删除和简化对象热度追踪。读完本文你将掌握 laddr 每一比特位的含义、各字段容量边界、冲突处理策略以及 multi-push recovery 场景下临时对象前缀约束的实现细节。背景laddr 与 LBA B-tree 的定位SeaStore 是 Ceph 新一代 OSD 存储引擎 Crimson 的本地对象存储层其事务模型将磁盘空间划分为物理地址paddr与逻辑地址laddr两层。laddr即 LBA B-tree 的键是 extent 定位流程的核心部分逻辑 extent 通过 LBA B-tree 映射到物理 extent而 LBA B-tree 的叶子节点存储的正是从 laddr 到 paddr 的映射。传统实现中laddr 的分配依赖laddr hint一个逻辑 extent 通常由一个 onode 派生而来因此分配 laddr 时需要借助对应 onode 的信息构造一个 hint再用它去搜索 LBA B-tree 以找到合适的地址。并非所有逻辑 extent 都与 onode 相关但这类 extent 只占总量的一小部分设计上可以暂时忽略。传统 64 位 laddr hint 的缺陷旧版 laddr hint 的位布局如下注意每个 laddr 代表磁盘上一个 4KiB 物理块[ec_shard:8][pool:8][crush:32][zero:16]方括号对表示存储于 laddr 整数中的一项属性每项包含属性名与占用的比特数。当 laddr 分配发生冲突时旧策略是从 hint 开始线性地向前backwards搜索空闲空间。文档指出该布局与冲突策略存在以下问题pool 字段过短按惯例 pool id 大于 127 的空间用于临时恢复数据导致普通对象数据与临时恢复数据混在一起当 pool id 大于 255 时不同 pool 的数据也会相互混合。ec_shard 字段过长实际几乎不可能为 EC 使用超过 128 个 shard8 比特属于浪费。缺少 snap 信息对 onode 做快照时各快照的 hint 完全相同导致克隆过程低效。地址空间过小一个 hint 最多表示 256MiB 逻辑地址空间当某个 onode 的快照数超过 16 个时每个 onode 预留 16MiB且未考虑同一 onode 下的元数据 extent就会与其他 onode 的 laddr hint 发生冲突。crush 字段高位是 pg id这使 laddr 在 pool 级别分布均匀但在每个 OSD 上 laddr 分布稀疏剩余比特在 pg id 增长时会使冲突更加频繁。基于以上问题可以得出结论如果要在 laddr 内部保留语义信息例如同一 pg 下的对象共享同一 laddr 前缀64 位长度是不够的。128 位静态布局 laddr 设计总览新设计中laddr 内部使用128 位整数表示地址值。除继承自整数的基础属性强序比较、算术运算外特定类型的 laddr 还携带从用户数据RADOS 对象派生的属性。静态布局设计保证了这些属性的确定性deterministic与可预测性predictable从而允许基于 laddr 做进一步的优化。laddr 的布局由三部分组成[upgrade:1][object_info:76][object_content:51]当不同对象的 object info 在一个 OSD 内互不冲突时可以得到一个非常有用的性质每个 RADOS 对象及其 head/clone 在一个 OSD 内拥有唯一的 laddr 前缀。基于这一性质可以做到将同一 RADOS 对象的不同快照归组到同一 laddr 前缀下加速克隆过程若某个 head/clone 的对象数据/元数据共享同一前缀删除该 head/clone 时可以通过范围删除完成无需保存完整对象名仅凭 laddr 即可追踪高频访问对象。在源码中这一设计落实为laddr_t类。其底层整数类型在支持__int128的平台上使用unsigned __int128否则退化为boost::multiprecision::uint128_t见 src/crimson/os/seastore/seastore_types.h 中的using Unsigned声明。laddr_t同时声明UNIT_SHIFT 12、UNIT_SIZE 4096印证了一个 laddr 代表一个 4KiB 块的约定。Object Info76 位对象语义信息Object Info 的定义如下[ec_shard:6][pool:12][reverse_hash:32][local_object_id:26]其中ec_shard、pool、reverse hash来自 RADOS 对象的信息local object id是一个随机数用于在 seastore 内部标识唯一对象。同一 pool 内两个不同的 RADOS 对象不应共享相同的 reverse hash 与 local object id这一点由分配过程保证。Object info 所有位全为 1 表示非法前缀RADOS 逻辑 extent 必须避免使用它以免与L_ADDR_NULL共享前缀。源码中L_ADDR_NULL被定义为L_ADDR_MAX即 128 位全 1见 seastore_types.h。全局元数据逻辑 extent 的两条特殊规则对于全局元数据逻辑 extent存在两条特殊规则RootMetaBlock 与 CollectionNode当 laddr 用于表示这两类 extent 时所有 object info 位应为 0且 RADOS 对象数据不得使用此前缀SeastoreNodeExtent这类 extent 用于存储 ghobject其 hint 派生自所存储 ghobject 的第一个槽位此时local object id 与 local clone id 应为 0RADOS 对象数据同样不得使用此前缀。注意ec_shard、pool、hash三者可能为 0此时允许与 RootBlock、CollectionNode 混合使用。这些规则在源码中体现为laddr_t的三个判定方法is_global_address()object info 全 0、is_onode_extent_address()local object id 为 0即LOCAL_OBJECT_ID_ZERO该常量被保留给 SeastoreNodeExtent、is_object_address()既非全局地址、也非 onode extent 地址、且 object info 不为全 1见 seastore_types.h。容量计算对于一个恰好包含 64k 个 pg 的 pool该布局允许2¹² 4096个 pool 每集群2⁶-1 63个 EC shard 每 pool2¹⁶ 65536个 pg 每 pool 与 OSD同一 pg 内的对象共享reverse_hash前缀当有 64k 个 pg 时该前缀为reverse_hash的前 16 位2⁴² 4T个对象每 pgreverse_hash剩余 16 位 26 位 object id需要强调的是Seastore 内部并不区分reverse_hash中属于 pg 前缀的比特——它只是依赖同一 pg 内对象共享 reverse_hash 前缀这一事实将任意给定 pg 的块排序在一起。pg 越多每 pg 的对象空间越小pg 越少每 pg 的对象空间越大。Object Content51 位数据定位信息全局元数据逻辑 extent 直接把这些位用作块地址而 RADOS 对象进一步将这部分位划分为[local_clone_id:23][is_metadata:1][blocks:27]local clone id与 local object id 类似RADOS 对象的每个 clone/快照在同一对象 laddr 前缀下拥有唯一的 local clone id。创建新快照时随机取一个 local clone id 作为该快照对象的新基地址。local clone id 位同样不能全为 1。is_metadata为 true 时剩余位表示 Omap*Node 的地址分配新 omap extent 时随机取值作为地址。为 false 时剩余位表示 ObjectDataBlock 的地址。blocks剩余 27 位表示具体数据 extent 的地址每个地址代表磁盘上的一个 4KiB 块。该布局允许2²³ 8M个 clone 每对象2²⁷ 128M个块每 clone128M × 4KiB 512GiB间接映射与 OMap 扇出优化文档特别指出clone 对象的间接映射只能存储其中间键的 local clone id参见pladdr_t::build_laddr()。源码中pladdr_t是一个std::variantlocal_clone_id_t, paddr_t其build_laddr()实现为key.with_local_clone_id(get_local_clone_id())即用所存 local clone id 还原出真正的中间键见 seastore_types.h。此外OMapInnerNode 有可能在每个 extent 中只存储块偏移字段而非完整的 128 位 laddr 来定位其子节点这可以增加 OMap 内部节点的扇出fan-out。冲突率Conflict Ratiolocal object id、local clone id 与元数据块的分配目前都需要随机选取。设计期望成功率约90%这样地址分配不会造成性能问题。90% 的成功率对应以下规模上限每 pg 对象数 400G每对象 clone 数 800K对象元数据量 50GiB在源码中冲突处理被建模为两个枚举laddr_conflict_condition_t定义冲突发生的条件包括object_prefix_at_object_id固定 shard/pool/reversed_hash分配唯一 local object id、clone_prefix_at_clone_id固定对象前缀分配唯一 local clone id、all_at_object_content、all_at_block_offset、all_at_neverladdr_conflict_policy_t定义冲突处理策略包括linear_search沿 LBA 迭代器线性搜索仅all_at_object_content与all_at_block_offset可用与gen_random生成新的随机 hint见 seastore_types.h。laddr_hint_t::find_next_random()实现了随机重试逻辑对于object_prefix_at_object_id循环重设 local object id 直到得到合法对象地址对于clone_prefix_at_clone_id同理处理 local clone id见 seastore_types.cc。laddr_hint_t还提供了 8 种合法的 hint 构造场景fresh 对象写数据/写元数据、克隆已有 onode 等对应create_fresh_object_data_hint、create_fresh_object_md_hint、create_clone_object_data_hint、create_clone_object_md_hint、create_object_data_hint、create_object_md_hint等工厂方法。Upgrade 位布局升级的 1 位选择器upgrade是 1 比特保留用于布局更新它存储当前活跃的 laddr 布局版本。布局升级期间映射使用活跃位upgrade0或upgrade1进行迁移从而允许新旧布局共存。在后续升级中该位可能再次翻转或在升级完成后回到 0。设计文档同时留下了一个 TODOfsck/工具必须支持在两次升级之间扫描并迁移所有映射。这提示该机制目前仍处于演进中实际部署前需要配套工具链支持。在源码的位布局中UpgradeFlagSpec 被定义为FieldSpec1, 127即 128 位中的最高位且注释明确upgrade 位的位置永远不会改变The upgrade bit position never changes见 seastore_types.h。Multi-push Recovery临时恢复对象的前缀约束当对象的 recovery/backfill 需要多次推送multiple pushes时需要为它创建一个 temp-recovering 对象。由于该临时对象可能执行clone_range并与其正在恢复的对象共享部分范围而间接映射的键必须与其直接映射的对应键共享前缀因此设计强制要求temp-recovering 对象必须与正在恢复的对象拥有相同的对象前缀唯一的例外是pool字段。这通过新增的ceph::os::Transaction::OP_TOUCH_TEMP操作实现该操作以传入的目标对象为参照创建一个对象前缀相同的临时对象临时对象的pool字段仍是临时 pool 的 id。源码实现位于 src/os/Transaction.hOP_TOUCH_TEMP 44touch_temp()接收 cid、临时对象 oid 与目标对象 oid将dest_oid记录到操作中注释明确写道与 OP_TOUCH 不同temp-recovering 对象必须与目标对象共享相同的 laddr 对象前缀参见 doc/dev/crimson/seastore_laddr.rst 的 Multi-push Recovery 一节。对应 hint 工厂create_temp_object_data_hint/create_temp_object_md_hint在 seastore_types.cc 中实现它们使用目标对象的 shard/pool/reversed_hash/local object id 构造 hint仅通过 clone 前缀上的冲突条件区分。三种逻辑 extent 的布局总结综合全文不同类型逻辑 extent 的 128 位 laddr 布局如下RootMetaBlock 与 CollectionNode全局元数据[upgrade:1][all_zero:76][offset:51]SeastoreNodeExtent存储 ghobject[upgrade:1][ec_shard:6][pool:12][reverse_hash:32][zero:26][offset:51]RADOS extentOMapInnerNode、OmapLeafNode、ObjectDataBlock[upgrade:1][ec_shard:6][pool:12][reverse_hash:32][local_object_id:26][local_clone_id:23][is_metadata:1][blocks:27]其中 RADOS extent 的local object id 必须非零。这套布局在源码中被编码为layout_v1结构体seastore_types.h各字段规格为ShardSpec FieldSpec6, 121、PoolSpec FieldSpec12, 109、ReversedHashSpec FieldSpec32, 77、LocalObjectIdSpec FieldSpec26, 51、LocalCloneIdSpec FieldSpec23, 28、MetadataFlagSpec FieldSpec1, 27、BlockOffsetSpec FieldSpec27, 0并定义了三级前缀掩码OBJECT_PREFIX_MASKupgrade object info、CLONE_PREFIX_MASK再加上 local clone id、MD_PREFIX_MASK再加上 is_metadata 位与文档中的前缀概念一一对应。结语与阅读指引128 位静态布局 laddr 是 SeaStore 逻辑寻址的核心设计它用地址即语义的方式把 RADOS 对象的 pool、EC shard、pg hash、对象/克隆标识编码进逻辑地址本身从而换取更快的克隆、范围删除与热度追踪能力同时通过upgrade位保留了后续布局演进的空间。需要注意的是本地对象 id、克隆 id 与元数据块分配目前依赖随机选取约 90% 成功率多推送恢复仍依赖OP_TOUCH_TEMP操作与配套 fsck 迁移工具的完善。感兴趣的读者可以继续深入以下仓库资源设计文档原文doc/dev/crimson/seastore_laddr.rstladdr 类型、位布局与 hint 定义src/crimson/os/seastore/seastore_types.hhint 构造与随机重试实现src/crimson/os/seastore/seastore_types.ccLBA 树管理器冲突策略的消费方src/crimson/os/seastore/lba/btree_lba_manager.h 与 src/crimson/os/seastore/lba/btree_lba_manager.ccOP_TOUCH_TEMP事务操作src/os/Transaction.h【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址: https://gitcode.com/gh_mirrors/ce/ceph创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考