Ceph RGW 多站点动态 Reshard 设计解析:基于布局代际(Generation)的独立分片治理

发布时间:2026/9/24 20:50:43
Ceph RGW 多站点动态 Reshard 设计解析:基于布局代际(Generation)的独立分片治理 存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载导读本文基于 Ceph 仓库中 src/doc/rgw/multisite-reshard.md 这一设计文档系统梳理 RGWRADOS Gateway在多站点复制场景下的动态 Reshard 方案。核心思路是让每个 zone 独立决定桶的分片shard布局同时保持复制日志的连续性避免跨 zone 协调与全量同步。通过引入布局代际generation、索引布局 / 日志布局分离、逐代际增量同步等一系列机制该设计把桶索引重分片对多站点数据同步的影响降到最低。读完本文你将理解 RGW 多站点 reshard 的设计约束、对象命名与元数据变化、日志处理策略、数据同步状态机的演进以及配套的 Admin API 变更并能结合仓库源码如 rgw_bucket_layout.h、rgw_reshard.cc深入验证实现细节。一、背景与核心需求为什么多站点 Reshard 如此棘手在单站点 Ceph RGW 中桶重分片bucket reshard是一个成熟能力当桶内对象数量增长导致桶索引bucket index某个 shard 承载过重时radosgw 会将该桶迁移到更多 shard 的新布局上。但在**多站点multisite**架构下这件事变得复杂因为每个 zone 都独立维护桶索引和复制日志bilogzone 之间通过**元数据同步metadata sync与数据同步data sync**保持一致桶索引 shard 对象名.dir.instance-id.shard-id与实例 IDinstance-id强相关而数据同步依赖实例 ID 在各 zone 间一致。原设计文档为此明确了三条硬性需求每个 zone 独立管理桶重分片决策基于 per-bucket 复制策略某些 zone 可能只复制对象子集、需要更少的 shard同时避免跨 zone 协调 reshard 的复杂性。重分片不要求对象的全量同步已有的 bilog 必须保留并被优先处理之后才轮到新 bilog shard。向后兼容任何 zone 都不能在其余 peer zone 升级到受支持版本之前 reshard并且需要一次手动的 zonegroup 变更来启用 resharding。其中第 3 点对应仓库中的zone feature 机制。在 rgw_zone_features.h 中定义了名为resharding的 zone feature 字符串并由新 zonegroup 默认启用enabled列表包含resharding与notification_v2而是否支持由supported列表决定// src/rgw/rgw_zone_features.h inline constexpr std::string_view resharding resharding; inline constexpr std::string_view compress_encrypted compress-encrypted; inline constexpr std::string_view notification_v2 notification_v2; // 本 release 支持的 feature inline constexpr std::initializer_liststd::string_view supported { resharding, compress_encrypted, notification_v2, }; // 新 zonegroup 默认启用的 feature inline constexpr std::initializer_liststd::string_view enabled { resharding, notification_v2, };从源码结构看reshardingfeature 正是要求 peer zone 已升级到支持版本 手动 zonegroup 变更的落地机制只有在所有 zone 都声明支持该 feature 后多站点动态 reshard 才能被安全启用。二、Layout 概念一切布局的抽象基石设计文档给出的定义一个 layout 描述一组 rados 对象以及一种把这些对象数据/条目分布到它们之上的策略。桶索引布局bucket index layout通过ceph_str_hash_linux()把对象名分布到若干 shard 上配合cls_rgw进行键的写入/删除日志布局datalog/bilog layout当前配合cls_log追加与裁剪日志条目未来可过渡到基于cls_queue或cls_fifo等其他原语的布局。Reshard 本质就是从一个 layout 到另一个 layout 的迁移transition。这意味着布局必须能够描述旧与新两套状态以及两者之间的过渡进度。仓库中 rgw_bucket_layout.h 把这一概念落成了具体的 C 类型体系BucketIndexTypeNormal基于哈希的分片索引布局或Indexless无桶索引不支持 listingBucketHashType目前只有Mod对对象名做 rjenkins 哈希后对num_shards取模bucket_index_normal_layout核心字段num_shards、min_num_shards、hash_type其中min_num_shards是该布局允许的最小分片数bucket_index_layout对bucket_index_normal_layout的封装bucket_index_layout_generationgen代际号layout即第 N 代索引布局。值得注意的细节num_shards()辅助函数对老桶做了兼容处理——历史桶用num_shards0表示 1inline uint32_t num_shards(const bucket_index_normal_layout index) { // old buckets used num_shards0 to mean 1 return index.num_shards 0 ? index.num_shards : 1; }此外is_layout_reshardable()判定只有Normal类型的索引布局才能被 reshard这解释了设计文档各 zone 独立管理 reshard在代码层面的约束边界。三、桶索引 Reshard实例私有分片信息与对象命名改造3.1 为什么不能沿用新建实例方案单站点 reshard 的做法是创建一个带目标分片布局的新 bucket instance迁移完成后切换到该实例。但在多站点中元数据主 zonemetadata master zone对全部桶元数据拥有权威包括分片布局与 reshard 状态任何元数据变更都必须在主 zone 发生并复制到其他 zone。若允许各 zone 各自新建 bucket instance会破坏数据同步对实例 ID 一致性的依赖若允许元数据同步用主 zone 的分片信息覆盖本地则各 zone 又无法独立决策。因此设计结论是桶的分片信息必须对本地 zone 的 bucket instance 私有并且需要同时记录当前分散在旧实例 新实例两份元数据中的全部 reshard 状态——旧 shard 布局、新 shard 布局、当前 reshard 进度。实现上只需阻止元数据同步覆盖这些私有字段即可。3.2 对象命名引入 Generation 号分片信息私有化直接影响桶索引 shard 的 rados 对象名。当前形式为.dir.instance-id.shard-id为了在同一个 instance-id 下表达多套分片布局对象名需要加入唯一标识——代际号generation每次 reshard 递增一次.dir.instance-id.generation.shard-id为保持向后兼容第 0 代generation0的对象名省略代际号即老对象名不变.dir.instance-id.0.0 → .dir.instance-id.0 gen 0 省略 .dir.instance-id.1.3 → gen 1 的 shard 33.3 源码印证BucketLayout 承载全部状态rgw_bucket_layout.h 中的BucketLayout结构正是文档所述的单一实例内保存全部 reshard 状态的载体struct BucketLayout { BucketReshardState resharding BucketReshardState::None; // 当前桶索引布局 bucket_index_layout_generation current_index; // 一次 reshard 操作的目标索引布局 std::optionalbucket_index_layout_generation target_index; // 未裁剪干净的日志布局代际历史当前代在 back() std::vectorbucket_log_layout_generation logs; // 用于判定桶是否处于 reshard 中配合锁超时判断 ceph::real_time judge_reshard_lock_time; };其中resharding字段枚举BucketReshardStateNone/InProgress/InLogrecord对应无操作 / 迁移中 / 正在记录日志条目current_index与target_index分别保存旧、新两套索引布局logs是未裁剪日志布局的历史列表最新代在末尾——这正是下一节日志布局历史的数据基础。此外 rgw_reshard.h 中定义了代际历史长度的硬上限// for multisite, the RGWBucketInfo keeps a history of old log // generations until all peers are done with them. prevent this log // history from growing too large by refusing to reshard the bucket // until the old logs get trimmed static constexpr size_t max_bilog_history 4;对应文档中为了防止历史无限增长在日志裁剪追上之前拒绝 reshard 桶索引日志的约束实现为 rgw_reshard.cc 中的判定函数bool RGWBucketReshard::should_zone_reshard_now(const RGWBucketInfo bucket, const RGWSI_Zone* zone_svc) { return !zone_svc-need_to_log_data() || bucket.layout.logs.size() max_bilog_history; }即非数据日志 zone 可以直接 reshard否则必须等 peer zone 消化完旧日志、logs历史回落到上限以内。四、桶索引日志BilogReshard日志与索引解耦4.1 为什么日志不能像普通键那样重分片多站点的桶复制日志与它们所修改的键存储在同一个桶索引 shard 中。但日志条目不能像普通键一样被重新洗牌其他 zone 需要跟踪自己在日志中的位置marker若把日志条目在各 shard 间打乱旧 marker 无法与新 shard 对应peer zone 唯一的选择就是重启全量同步。因此设计定论重分片时必须保留旧桶索引日志让其他 zone 可以继续处理旧日志条目同时新事件写入新的桶索引日志。4.2 新目标复制日志搬出 omap文档还提出了一个附加目标把复制日志从 omap即桶索引内部迁移到独立的 rados 对象基于 cls_fifo / cls_queue 等原语。为此bucket instance 元数据必须能描述一个索引布局与日志布局不同的桶。对已有桶两者相同并共享桶索引对象其他形式的日志布局如 FIFO不在本文设计范围之内。这一目标在代码中体现为BucketLogType枚举enum class BucketLogType : uint8_t { InIndex, // 0: 与桶索引同址日志布局跟随索引布局 Deleted, // 1: 日志代际已被移除 FIFO, // 2: 独立的 FIFO 对象 };以及bucket_log_layout/bucket_log_layout_generation结构其中bucket_log_layout_generation由gen日志代际号layout组成与索引布局的代际号互相独立。设计还提供了两个辅助函数log_layout_from_index(gen, index)生成一个与索引布局共享布局的InIndex日志布局fifo_log_layout_from_index(gen, index)生成一个独立的 FIFO 日志布局其分片数由bilog_shards_for_index()计算——采用对数公式索引分片数翻倍时 bilog 只增加 2 个 shard默认区间约 721// compute an appropriate number of bilog shards for a given index shard count // upon reshard. uses logarithmic formula to keep the bilog shard count much lower, // growing slowly every time index shards double, bilog gets 2 more shards. inline uint32_t bilog_shards_for_index(uint32_t index_shards, uint32_t min_shards 7, uint32_t max_shards 0)4.3 代际历史的生命周期为了让仍在处理旧日志的 peer zone 正常工作本地 bucket instance 元数据必须跟踪所有尚未完全裁剪的日志布局历史一旦某代日志的裁剪越过旧代就删除关联的 rados 对象并从 bucket instance 元数据中移除该代日志布局为防止历史无限增长在裁剪追上之前拒绝继续 reshard见上节max_bilog_history 4。4.4 索引布局与日志布局分离的意义文档强调区分index layout与log layout极其重要因为增量同步只关心日志布局的变化全量同步只关心索引布局的变化。传统全量同步使用自定义的RGWListBucket扩展逐个列出索引 shard 的对象如果把全量同步的范围从按桶 shard改为按桶即使用普通 bucket listing 获取全部对象全量同步就与索引布局无关一旦复制日志搬出桶索引动态 reshard 就可以任意改变索引布局而对多站点复制零影响。五、设计落地八项核心任务拆解原文档以 Tasks 形式给出了完整的工程化拆解以下逐一结合仓库证据说明。5.1 桶 ReshardBucket Reshard改造现有 reshard 状态机由新建 bucket instance改为在原 bucket instance 上原地变更。对日志仍在索引中的桶执行 reshard 时需要按序完成在 bucket instance 上新增一代日志布局log layout generation把桶索引条目复制到新索引布局提交日志代际变更使新条目写入新代日志创建一条携带新日志代际的 datalog 条目。实现上rgw_reshard.cc 中RGWBucketReshard::do_reshard()、reshard_process()以及BucketReshardManager目标分片管理器正是这套状态机的核心reshard_process以当前索引布局 → 目标索引布局为参数逐批迁移条目支持max_entries批大小限制与ReshardFaultInjector故障注入便于测试恢复路径。目标分片数优先取质数reshard_primes列表与get_prime_shards_less_or_equal()/get_prime_shards_greater_or_equal()/nearest_prime()等工具函数保证分片数为质数、哈希分布更均匀。5.2 元数据同步Metadata Sync从主 zone 同步 bucket instance 时保留本地实例的私有字段使用cls_version保证写回的是这些私有字段的最新版本防止多写者竞态覆盖。5.3 数据同步Data Syncdatalog 条目目前只包含桶 shard 号需要追加日志代际号以区分条目所属的分片布局若看到新的代际号该条目还隐含一项义务必须完成旧代所有 shard 的同步后才能进入新代。5.4 桶同步状态Bucket Sync Status新增按桶per-bucket的同步状态对象跟踪全量同步进度当前增量同步的代际已完成该代增量同步的 shard 集合。原有按桶-shard 的同步状态对象继续跟踪增量同步且对象名需包含代际号第 0 代除外。向后兼容的 ENOENT 处理逻辑若远端最旧日志布局的generation0则读取现存 per-shard 状态对象有则从那里恢复增量同步否则初始化为全量同步。该状态对象与cls_version配合检测来自不同 shard 的并发写入见 5.5。仓库中rgw_read_bucket_full_sync_status等函数声明于 rgw_data_sync.h实现在 rgw_data_sync.cc以及 rgw_rest_log.cc 中RGWOp_BILog_Status::execute()对bilog_status_v2.status.sync_status的填充正是这套按桶同步状态在 Admin API 侧的读取路径。5.5 桶同步Bucket Sync全量同步改为一次桶级 listing抓取全部对象用cls_lock防止不同 shard 重复这份工作增量同步读到某日志 shard 末尾truncatedfalse时若远端存在更新的日志代将该 shard 标记为当前代的done当前代全部 shard 达到done后桶增量同步进入下一代在桶同步状态对象上用cls_version检测来自其他 shard 的竞争写入。5.6 桶同步的启用/禁用Bucket Sync Enable/Disable文档建议把该流程重构为围绕日志代际的表述取代原来用 SYNCSTOP 事件配合特殊Stopped状态的做法radosgw-admin bucket sync enable在 bucket instance 元数据中创建一代新的日志布局与 reshard 的竞态检测若 reshard 正在进行则失败写入时使用cls_version检测与 reshard 开始时刻的竞争若当前日志代与桶索引布局共享BucketLogType::InIndex新日志代会指向相同的索引布局/代际——即日志代际递增但索引对象保持原代际不变。增量同步中的 SYNCSTOP将该 shard 标记为done在见到新代际前忽略该桶的 datalog 事件。仓库 svc_bilog_rados.cc 中可以看到向各 shard 推送CLS_RGW_OP_RESYNC与CLS_RGW_OP_SYNCSTOP控制条目的实现印证了日志代际与同步启停控制项在底层对象层的交互。5.7 日志裁剪Log Trimming用同步状态中的代际号裁剪对应日志某代日志的全部 shard 均裁剪完成后删除它们的 rados 对象删除关联的增量同步状态对象从 bucket instance 元数据中移除该日志代。5.8 Admin API 扩展原文档要求三处 Admin API 响应扩展均已在 rgw_rest_log.cc 中找到对应实现Admin API / RGWOp新增响应内容目的RGWOp_BILog_List桶的最高日志代际highest log generation让增量同步判断truncatedfalse是已追上还是需要切到下一代RGWOp_BILog_Info桶的最低与最高日志代际让桶同步状态初始化决定是否需要扫描既有 shard 状态以及全量同步完成后从哪里恢复增量同步RGWOp_BILog_Status按桶的状态信息供旧代日志裁剪决策RGWOp_BILog_List的请求参数已支持generations-info.args.get(generation, gen_specified)并在send_response_end()中当存在更新一代时输出next_log节包含generation与num_shards正是最高日志代际这一信息的序列化出口RGWOp_BILog_Status在version1时携带bilog_status_v2包含全量同步状态等按桶信息。六、设计要点小结维度设计决策源码依据决策权每 zone 独立管理 reshard主 zone 元数据权威仅限公共字段rgw_bucket_layout.hBucketLayout实例一致性原地变更 bucket instance不新建实例私有分片字段防元数据同步覆盖rgw_reshard.cc对象命名.dir.instance-id.generation.shard-idgen 0 省略设计文档 §Bucket Index Resharding日志连续性保留旧代日志供 peer 处理新事件写新代裁剪完才删除max_bilog_history 4rgw_reshard.h布局分离index layout 与 log layout 解耦支持InIndex/FIFOrgw_bucket_layout.hBucketLogType同步状态per-bucket 状态 per-shard 状态cls_version防竞态rgw_data_sync.cc全量同步桶级 listing cls_lock防重复与索引布局解耦设计文档 §Bucket Sync启用门控zone featureresharding 手动 zonegroup 变更rgw_zone_features.h七、从设计到实现的验证路径对希望深入阅读源码的读者建议按以下顺序研读布局类型体系rgw_bucket_layout.hBucketLayout、bucket_log_layout_generation、BucketLogType、bilog_shards_for_indexReshard 状态机rgw_reshard.h 与 rgw_reshard.ccRGWBucketReshard、RGWReshard线程、RGWBucketReshardLock锁、max_bilog_history门控日志读写服务svc_bilog_rados.cc按代际的日志读写、CLS_RGW_OP_RESYNC/CLS_RGW_OP_SYNCSTOP控制条目、FIFO 惰性创建数据同步状态机rgw_data_sync.h 与 rgw_data_sync.ccper-bucket 全量/增量同步状态、cls_version并发控制Admin APIrgw_rest_log.ccRGWOp_BILog_List/Info/Status的代际相关字段功能门控rgw_zone_features.hreshardingfeature。这套设计把分片布局提升为可以独立演进的代际化概念使多站点复制从一次 reshard 即牵一发动全身演进为每个 zone 按需伸缩、peer 无缝跟随是理解现代 Ceph RGW 多站点运维与数据同步架构的关键一环。赞分享存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载相关推荐Ceph Dumplingv0.67版本发布全解析多站点 RGW、ceph-rest-api 与升级路径指南Ceph Dumplingv0.67版本发布全解析多站点 RGW、ceph rest api 与升级路径指南 Dumpling 是 Ceph 分布式存储平存储分布式文件系统对象存储后端高可用Ceph RGW 非阻塞 Bucket Reshard 深度解析两阶段 logrecord 机制设计与源码实现Ceph RGW 非阻塞 Bucket Reshard 深度解析两阶段 logrecord 机制设计与源码实现 Ceph RGWRADOS Gateway存储分布式文件系统对象存储后端高可用Ceph MGR RGW 模块实战指南用 ceph rgw realm/zone 命令一键编排多站点部署Ceph MGR RGW 模块实战指南用 ceph rgw realm/zone 命令一键编排多站点部署 导读 本文围绕 Ceph Manager 守护进程内存储分布式文件系统对象存储后端高可用上一篇基于 Loco SaaS Starter 模板构建 Rust 全栈应用JWT 认证与前后端渲染配置实战下一篇Amlogic S9XXX Armbian 终极刷机指南让电视盒子变身强大服务器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考