ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ceph RGW Rados Bucket Index 深度解析:索引模型、版本化、事务一致性与分片重分片

Ceph RGW Rados Bucket Index 深度解析:索引模型、版本化、事务一致性与分片重分片 存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载导读本文基于 Ceph 开源仓库中的 doc/dev/radosgw/bucket_index.rst系统解析 RGWRADOS Gateway的 Rados Bucket Index 机制。Bucket Index 是每个存储桶Bucket内对象列表及其元数据size、etag、mtime 等的索引是支撑 S3ListObjectsV2/ListObjectVersions与 SwiftGET Container等列目录 API 的底层数据结构。读完本文你将掌握 bucket index 的存储模型、S3 版本化下的 olh 间接寻址、读写一致性保证、索引事务协议、分片与动态重分片机制以及基于radosgw-admin的手动运维手段。什么是 Bucket Index在 RGW 中每个存储桶都维护一份bucket index桶索引用于存放该桶内对象列表及每个对象的相关元数据size、etag、mtime 等从而支撑对象列举类 API 请求并承担部分内部记账bookkeeping职责。这些 API 对应 S3 的ListObjectsV2、ListObjectVersions以及 Swift 的GET Container。存储载体RADOS omap索引条目并非存放在独立文件中而是存放在索引对象或按分片拆分的多个索引对象的RADOS omap条目中。omap 是 RADOS 对象上以键值对形式存储的扩展属性集合天然适合存放“对象名 → 元数据”这种字典结构。Indexless 桶存储桶也可以被创建为indexless无索引模式。此类桶没有索引因此无法进行列目录操作。在源码中对应 src/rgw/rgw_bucket_layout.h 的BucketIndexType枚举enum class BucketIndexType : uint8_t { Normal, // normal hash-based sharded index layout Indexless, // no bucket index, so listing is unsupported };可以看到Indexless的注释明确指出 no bucket index, so listing is unsupported。而is_layout_reshardable()等辅助函数rgw_bucket_layout.h也以BucketIndexType::Normal作为可重分片的前提。索引条目的粒度对于非版本化存储桶bucket index 中每个对象对应一个条目。对于 S3版本化存储桶则更为复杂详见下一节每个对象版本和删除标记delete marker都对应一个索引条目。S3 对象版本化与 olh 间接寻址版本化存储桶的 bucket index 中除了按对象名排序的条目外还为同名对象的各个版本维护了一组从新到旧排序的条目供版本化列目录使用。这解释了为何版本化桶在RGWRados::calculate_preferred_shards()src/rgw/driver/rados/rgw_rados.cc中会提前触发重分片——源码注释说明每个版本化桶的第一个对象需要 4 个索引条目之后每新增一个对象需要 2 个条目max_objs_per_shard / 3因此索引增长远快于普通桶。head 对象与版本 oidRGW 在rgw.buckets.data池中为每个对象版本存放一个 head 对象。该 RADOS 对象的 oid 由**对象名 版本 idversion id**组合而成从而保证不同版本对应不同的 RADOS 对象。Object Logical HeadolhS3 的 GET/HEAD 请求按对象名访问时应返回该对象的“当前current”版本。为了支持这一点RGW 额外保存一个object logical headolh对象其 oid 只包含对象名不含版本 id作为指向当前版本 head 对象的间接层indirection。这段间接寻址逻辑实现在RGWRados::follow_olh()src/rgw/driver/rados/rgw_rados.ccint RGWRados::follow_olh(const DoutPrefixProvider *dpp, RGWBucketInfo bucket_info, RGWObjectCtx obj_ctx, RGWObjState *state, const rgw_obj olh_obj, rgw_obj *target, optional_yield y)从实现可见其核心流程收集 olh 对象属性中以RGW_ATTR_OLH_PENDING_PREFIX为前缀的 pending 条目即尚未收敛的版本写入/删除记录→ 检查并移除已过期的 pending 条目check_pending_olh_entries/remove_olh_pending_entries→ 若仍有 pending 条目则调用update_olh()将 olh 推进到最新版本 → 最后从RGW_ATTR_OLH_INFO中解码RGWOLHInfo将olh.target作为当前版本目标返回。若该 olh 标记为removed对象当前为删除标记则设置state-is_dm true并返回-ENOENT。保证 olh 与 bucket index 的一致性为了维持 olh 对象与 bucket index 之间的一致性索引为每个对象名保留一个独立的 olh 条目记录对其所有版本的全部写入/删除日志。当 RGW 需要依据索引判定“当前版本”时RGWRados::apply_olh_log()同样位于 src/rgw/driver/rados/rgw_rados.cc会重放replay这份日志保证 olh 对象收敛到与 bucket index 一致的“当前版本”。一致性保证Consistency GuaranteeRGW 在对象操作上保证读后写一致性read-after-write consistency一旦客户端收到某个写请求的成功响应该写入的效果必须对后续读请求可见。例如S3 客户端先发PutObject覆盖一个已存在对象、紧接着发GetObject读回时RGW不得返回旧对象内容必须返回新对象内容或返回其后某次写入/删除的结果。该保证适用于所有对象写请求PutObject、DeleteObject、PutObjectAcl等与所有对象读请求HeadObject、GetObject、ListObjectsV2等。这一强一致性约束正是后续“三步索引事务”设计见后文的根本动因。Rados 对象模型S3/Swift 对象即 API objects存放在rgw.buckets.data池中每个 API 对象由一个head 对象和零个或多个 tail 对象组成。而bucket index 对象则存放在独立的rgw.buckets.index池中。写入对象时head 对象最后写入作为使其对读请求可见的原子“提交commit”动作。将索引与数据分池、head 后写为下一步通过事务协议保证两者一致提供了物理基础。分片Sharding与重分片Resharding为什么需要分片一个桶的索引通常会被拆分为多个 RADOS 对象称为bucket index shards索引分片。在 RADOS 中对同一对象的多次写入无法并行把索引分散到更多 RADOS 对象上即可提高写入并行度。对于一次对象上传其对应的索引分片通过对象名的哈希选择enum class BucketHashType : uint8_t { Mod, // rjenkins hash of object name, modulo num_shards };见 src/rgw/rgw_bucket_layout.hMod模式即“对象名的 rjenkins 哈希值对分片数取模”。这一设计保证同一对象的所有条目及其在 S3 版本化桶中的所有版本必定落在同一分片上同时一般也能让对象均匀分布到各分片——但当某些对象拥有大量版本时均匀性会被破坏热点分片。分片数量配置新桶的默认分片数为 11可通过以下两个途径覆盖配置位置配置项说明zonegroupbucket_index_max_shards区域组级默认分片数ceph.confrgw_override_bucket_index_max_shards全局覆盖优先级更高源码佐证见 src/rgw/driver/rados/rgw_bucket.cc} else if (cct-_conf-rgw_override_bucket_index_max_shards 0) { ... cct-_conf-rgw_override_bucket_index_max_shards;以及 src/rgw/driver/rados/rgw_rados.cc 中bucket_index_max_shards对rgw_override_bucket_index_max_shards的优先读取逻辑。分片布局结构定义在 src/rgw/rgw_bucket_layout.h 的bucket_index_normal_layoutstruct bucket_index_normal_layout { uint32_t num_shards 1; // 当前分片数 uint32_t min_num_shards 1; // 该桶布局允许的最少分片数 BucketHashType hash_type BucketHashType::Mod; };注意旧桶曾用num_shards 0表示 1rgw::num_shards()辅助函数做了兼容归一化rgw_bucket_layout.h。重分片的必要性与触发条件任何 RADOS 对象的 omap 都有约 100,000 条目的实际上限因此必须保证分片数量足够、使每片条目数不超过该上限。随着桶内对象数增长可能需要reshard重分片以增加分片数。重分片存在两种方式手动重分片通过radosgw-admin管理命令主动触发动态重分片dynamic resharding无需管理员干预自动发生。动态重分片的判定逻辑位于RGWRados::check_bucket_shards()src/rgw/driver/rados/rgw_rados.cc关键流程为首先检查配置开关rgw_dynamic_resharding关闭则直接返回不触发校验布局可重分片is_layout_reshardable调用calculate_preferred_shards()结合rgw_max_dynamic_shards动态分片上限与rgw_max_objs_per_shard每分片目标条目数计算期望分片数版本化桶按 1/3 折算条目上限以提前触发若涉及缩减分片还受rgw_dynamic_resharding_may_reduce开关约束判定需要重分片时通过add_bucket_to_reshard()rgw_rados.cc写入重分片队列reshard log记录old_num_shards、new_num_shards、initiator Dynamic等信息交由后台进程执行。动态缩减分片与延迟等待动态重分片也可以减少分片数量。由于某些桶的对象数会快速增减且不希望频繁重分片去“追逐”这些波动缩减分片操作在“判定需要缩减”与“实际执行”之间引入了延迟到执行时刻会再次检查桶仅当桶仍确实需要缩减分片时才真正执行。该延迟默认 5 天可用 ceph.conf 中的rgw_dynamic_resharding_reduction_wait配置。源码实现见 src/rgw/driver/rados/rgw_reshard.cc读取该配置后换算为等待时间窗口。布局信息的载体桶的索引对象布局信息存放在RGWBucketInfo的struct rgw::BucketLayout中src/rgw/rgw_bucket_layout.h。该结构除记录当前索引布局current_index外还包含reshardingBucketReshardState枚举None/InProgress/InLogrecord标记重分片是否进行中target_index重分片操作的目标布局可选重分片期间存在logs未裁剪的桶日志bilog布局代际历史judge_reshard_lock_time用于判断桶是否正在重分片的锁时间戳。真正的重分片执行逻辑位于 src/rgw/driver/rados/rgw_reshard.cc。手动重分片radosgw-admin 命令radosgw-admin提供了一组完整的重分片运维命令src/rgw/radosgw-admin/radosgw-admin.cc 的命令清单命令用途radosgw-admin bucket reshard --bucketname --num-shardsN手动对指定桶重分片radosgw-admin bucket set-min-shards设置动态重分片会为桶考虑的最少分片数radosgw-admin reshard add将桶加入调度重分片队列radosgw-admin reshard list列出所有正在重分片或已调度的桶radosgw-admin reshard status读取桶的重分片状态radosgw-admin reshard process处理已调度的重分片任务radosgw-admin reshard cancel取消桶的重分片radosgw-admin reshard stale-instances list列出重分片遗留的 stale-instancesradosgw-admin reshard stale-instances delete清理重分片遗留的 stale-instancesradosgw-admin reshardlog list列出桶重分片日志radosgw-admin reshardlog purge裁剪桶重分片日志示例将mybucket强制重分片为 32 个索引分片radosgw-admin bucket reshard --bucketmybucket --num-shards32查看所有待重分片/进行中的桶radosgw-admin reshard list radosgw-admin reshard status --bucketmybucket注上述命令的具体行为与输出格式以当前仓库 radosgw-admin.cc 中的命令注册为准。索引事务Index Transaction问题两个对象无法原子更新所有对象写入或删除都必须同步更新 bucket index 以保持一致性。但 head 对象与 bucket index 存放在不同的 RADOS 对象分属rgw.buckets.data与rgw.buckets.index池中无法用单个 RADOS 操作原子地同时更新两者。为了满足列目录操作的一致性保证RGW 用三步桶索引事务协调这两次对象写入在 bucket index 对象上**预备Prepare**一个事务写入或删除 head 对象在 bucket index 对象上**提交Commit事务若第 2 步失败则取消Cancel**事务。pending 与 completed 状态对象写入与删除可能彼此竞争因此同一对象在某一时刻可能同时存在多个已预备但未提交的事务。RGW 将对象条目区分为两种状态pending挂起存在未完成事务completed完成不存在未完成事务。源码实现位置该事务协议在 src/rgw/driver/rados/rgw_rados.cc 中实现为RGWRados::Object::Write::write_meta()对象写入路径RGWRados::Object::Delete::delete_obj()对象删除路径。而 bucket index 侧的底层操作由 OSD 侧的 class 方法实现位于 src/cls/rgw/cls_rgw.ccrgw_bucket_prepare_op()cls_rgw.cc预备事务将条目标记为 pendingrgw_bucket_complete_op()cls_rgw.cc提交事务或在上游失败时回滚将条目推进到 completed。这些方法通过cls注册接口暴露给 radosgw 调用cls_rgw.cc 中的cls.register_cxx_method(...)注册片段形成 RGW 侧编排 OSD class 侧执行 的完整事务链路。列目录Listing列出条目与盘查 head列目录时RGW 会读取 bucket index 中的全部条目包括 pending 与 completed。对任何 pending 条目必须先检查对应 head 对象是否存在再决定是否将条目纳入最终列表——这保证了即使事务中断列表也不会出现幽灵条目。dir suggest从列目录到索引的反馈如果 RGW 在索引事务中途崩溃索引条目可能卡在 pending 状态。当后续列目录遇到这些 pending 条目时RGW 会把从 head 对象读到的信息回写给 bucket index用于更新条目、解决陈旧事务。这条回写消息称为dir suggest目录建议——因为 bucket index 把它当作一条提示/建议来采纳。源码实现位置列目录功能在 src/rgw/driver/rados/rgw_rados.cc 中实现为RGWRados::Bucket::List::list_objects_ordered()有序列目录RGWRados::Bucket::List::list_objects_unordered()无序列目录RGWRados::check_disk_state()读取 head 对象并编码建议的索引变更即生成 dir suggest 载荷。bucket index 侧的对应操作在 src/cls/rgw/cls_rgw.cc 中实现为rgw_bucket_list()cls_rgw.cc按前缀/游标等条件遍历 omap 条目rgw_dir_suggest_changes()cls_rgw.cc应用列目录回传的建议解决 stale pending 事务。有序列目录的高昂代价由于 RGW 对象依据哈希分布到桶的各索引分片上分片之间不存在词典序lexical ordering只有单个分片内部保持有序。因此有序列目录list_objects_ordered是一种复杂且 I/O 密集的操作从每个分片并行拉取一批条目使用**选择排序selection sort**在批次间归并产出一段有序结果当任一分的批次耗尽时从所有分片再次批量读取如此循环直至完成。这也意味着分片数量越多有序列目录的并行批次管理开销越大运维时需要在写入并行度分片多与有序列目录成本分片少之间权衡并借助动态重分片在桶规模变化时自动保持平衡。小结Rados Bucket Index 是 RGW 对象存储语义的基石它以 RADOS omap 承载对象元数据索引通过 olh 间接层支撑 S3 版本化语义以三步索引事务在数据与索引分池的约束下满足读后写一致性并以分片 动态重分片应对 omap 容量上限与写入并发需求。理解这些机制有助于在部署、排障与容量规划时做出正确的配置决策——例如合理设置rgw_override_bucket_index_max_shards、关注rgw_max_objs_per_shard与rgw_dynamic_resharding系列参数以及熟练使用radosgw-admin的重分片运维命令。赞分享存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载相关推荐Ceph RGW 数据布局深度解析元数据、Bucket 索引与对象寻址机制Ceph RGW 数据布局深度解析元数据、Bucket 索引与对象寻址机制 本指南以 Ceph 官方文档 doc/radosgw/layout.rst htt存储分布式文件系统对象存储后端高可用TiDB 整型分片索引Integer Shard Index设计与实现深度解析TiDB 整型分片索引Integer Shard Index设计与实现深度解析 本篇文章围绕 TiDB 设计文档 Integer shard index h数据库分布式数据库后端OLAPLance 碎片复用索引Fragment Reuse Index深度解析用延迟索引重映射化解 compaction 与索引优化冲突Lance 碎片复用索引Fragment Reuse Index深度解析用延迟索引重映射化解 compaction 与索引优化冲突 Lance 采用碎片数据库向量数据库数据湖全文检索创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表