ARTICLE DETAIL

资讯详情

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

GreptimeDB Global GC Worker 设计解析:基于 Compaction 驱动的文件垃圾回收机制

GreptimeDB Global GC Worker 设计解析:基于 Compaction 驱动的文件垃圾回收机制 GreptimeDB Global GC Worker 设计解析基于 Compaction 驱动的文件垃圾回收机制【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb本文依据 docs/rfcs/2025-07-23-global-gc-worker.md 展开并结合 GreptimeDB 当前仓库中 mito2 存储引擎的LocalGcWorker实现src/mito2/src/gc.rs、meta-srv 的 GC 调度器src/meta-srv/src/gc/scheduler.rs及对应配置项深入讲解该 GC 机制的设计动机、工作流程、源码实现与配置方式。读者读完后可以理解 GreptimeDB 如何在列式存储引擎中安全回收废弃 Parquet/Puffin 文件、如何借助临时 Manifest 与心跳机制保护长查询所需文件以及如何通过配置项调优 GC 行为。背景与动机为什么存储层需要全局 GCGreptimeDB 是基于对象存储的时序/可观测性数据库数据最终以 SSTParquet 数据文件与 Puffin 索引文件的形式落在对象存储上。随着功能的演进存储目录中会不断积累不再被任何组件使用的陈旧文件典型来源包括表重新分区repartition分区规则变更后旧分区对应的整批 SST 文件会被新文件替换产生大量废弃文件Manifest 更新失败新 SST 文件已成功写入对象存储但随后的 manifest 更新操作失败导致文件从未被任何 manifest 记录成为无人引用的孤儿文件Compaction / Truncate / Delete数据合并、清空与删除操作都会将旧文件标记为移除。如果不做回收这些文件会持续占用对象存储空间。因此RFC 提出在 Compaction 流程内集成一个垃圾回收GC机制当一个 region 的 Compaction 完成后自动触发 GC worker识别并删除超过保留期限的废弃文件。这一设计把 GC 的执行绑定在一个明确、可定义的操作边界——一次 compaction 周期的成功完成上以正确性和安全性为优先。术语定义RFC 将待回收文件划分为两类核心概念术语含义典型场景Unused File未使用文件存在于存储目录中、但从未被任何 manifest 正式记录的文件新 SST 成功写入存储但随后的 manifest 更新失败导致文件未被引用Obsolete File废弃文件曾经被 manifest 记录、但现已明确标记为移除的文件数据 repartition 或 compaction 后产生的旧文件仓库实现中进一步引入了两个与时间相关的精确概念见 src/mito2/src/gc.rs 的模块注释expel time驱逐时间文件被视为已移除的时刻即它从 manifest 中被移除的时间lingering time滞留时间文件从 manifest 移除后、到真正从对象存储删除之间的时间窗口。此外代码中还将未在 manifest 中记录的文件细分为unknown files因为 checkpoint 可能已经推进、移除了 checkpoint 之前的移除记录导致文件的 expel time 无法确定并为此单独设计了滞留策略。GC Worker 工作流程触发与执行位置RFC 明确GC worker 作为 Compaction 流程的组成部分运行。某个 region 的 Compaction 完成后GC worker 自动被触发执行位置优先放在datanode上从而避免 metasrv 侧需要额外配置对象存储object storage访问权限的负担。在实际仓库实现中GC 由两个层面协同驱动datanode 侧执行层LocalGcWorker 是真正执行文件扫描与删除的 worker它持有一个或多个待 GC 的 region同一张表并携带GcConfig、FileRefsManifest等上下文metasrv 侧调度层GcScheduler 周期性默认每 5 分钟一个 tick见 options.rs向 datanode 下发 GC 请求并汇总各 datanode 返回的GcReport。它还支持通过Event::Manually手动触发指定 region 的 GC。完整处理流程RFC 给出的流程如下触发region 的 Compaction 成功完成后调用 GC worker读取 Manifest读取 region 的主 manifest获得所有被标记为废弃的文件列表同时读取长查询产生的临时 manifesttemporary manifests识别当前正在使用的文件防止误删检查滞留时间Obsolete Files对每个废弃文件计算滞留时间即从 manifest 移除后经过的时间标记删除Obsolete Files超过可配置的最大滞留时间、且未被任何活跃临时 manifest 引用的文件标记为待删除滞留时间Unused Files从未被任何 manifest 记录的文件同样需要等待可配置的最大滞留时间之后才可删除。对应流程图RFC 原文源码级实现LocalGcWorker 的执行细节对照 src/mito2/src/gc.rsLocalGcWorker::run()按顺序执行以下步骤与 RFC 流程一一对应读取临时引用read_tmp_ref_files()从FileRefsManifest中收集所有 region 当前被引用的文件集合gc.rs逐 region 执行do_region_gc()内先获取 region 的主 manifest并校验临时 manifest 记录的 manifest version 与 region 当前 manifest version 是否一致。不一致例如 leader 刚更新了 manifest version时直接跳过该 region 的 GC避免误删仍在使用的文件gc.rs获取已移除文件及驱逐时间get_removed_files_expel_times()从 manifest 的removed_files记录中还原每个文件的 expel time即包含移除动作的 delta manifest 的最后修改时间见 gc.rs并发列出 region 目录文件list_from_object_store()将 region 目录按字典序边界分区使用多个 lister 并发列出 Parquet 文件并单独平铺列出region_dir/index/下的 Puffin 索引文件gc.rs过滤可删除文件filter_deletable_files()结合在 manifest 中在临时引用中处于滞留期已达可删除条件四类状态逐文件判定是否可删物理删除与状态更新delete_files()调用delete_files/delete_indexes真正删除对象存储上的数据与索引随后update_manifest_removed_files()调用clear_deleted_files清理 manifest 中对应的移除记录gc.rs。需要特别说明的是仓库实现中 GC 并不总是做全量文件列表扫描而是支持两种模式由LocalGcWorker.full_file_listing字段控制见 gc.rs快速模式fast仅删除 manifest 的removed_files中记录的文件避免昂贵的列表操作性能最佳作为常规 GC 使用全量列表模式full_file_listing完整扫描 region 目录额外发现并清理未被任何 manifest 跟踪的孤儿文件代价高周期性执行即可metasrv 侧默认每 24 小时执行一次全量列表见下文配置节。废弃文件Obsolete Files的回收条件RFC 明确规定一个废弃文件只有当以下两个条件同时满足时才会被永久删除从 manifest 中被移除其 obsolescence timestamp所经过的时间超过可配置阈值当前未被任何活跃的临时 manifest 引用。在源码中这一逻辑浓缩在should_delete_file()函数中gc.rs文件若在 manifest 中is_in_manifest或在临时引用中is_in_tmp_ref一律不删除文件若处于滞留期is_linger但未达删除条件is_eligible_for_delete不删除仅当is_linger且is_eligible_for_delete同时为真时才删除对于无法解析为 SST/索引文件名的条目直接跳过并累加GC_SKIPPED_UNPARSABLE_FILES指标绝不删除未知类型文件gc.rs。对应地src/mito2/src/gc/worker_test.rs 中提供了丰富的测试验证这些边界条件test_gc_worker_basic_compact写入多批数据并 compaction 后GC 删除的文件数量与 manifest 中记录的被移除文件数量一致test_gc_worker_compact_with_ref当文件被临时引用FileRefsManifest保护时GC 结果为空0 个文件被删test_known_file_still_lingering_not_deleted/test_known_file_eligible_for_delete_deleted分别验证滞留期未满不删、期满即删test_file_in_manifest_not_deleted/test_file_in_tmp_ref_not_deleted验证 manifest 与临时引用对文件的强保护。未使用文件Unused Files的处理RFC 指出由于 GC worker 与 Compaction 深度集成Unused Files这类最容易被误删的文件类别风险已被大幅化解——新写入的 SST 文件几乎总是紧接着伴随 manifest 更新因此写完文件但尚未记入 manifest的时间窗口极短。任何真正未使用未被任何 manifest 引用的文件在超过可配置的最大滞留时间后即可安全删除。不过源码实现比 RFC 更为保守将unknown files无法确定 expel time 的文件单独对待对于活跃/打开的 region仅当对象文件的last_modified时间超过unknown_file_lingering_time默认 1 天阈值时才删除且采用严格小于strict less-than比较如果对象存储不提供last_modified元数据则保守地保留该文件gc.rs对于已删除的 regiondroppedunknown 文件可以立即删除因为这类 region 已无活跃读写且 meta 侧在发起 dropped-region GC 前会先收集相关活跃 region 的FileRefsManifest以做交叉保护。对应测试见 worker_test.rs 中的test_unknown_file_within_ttl_not_deleted、test_unknown_file_exceeded_ttl_deleted、test_unknown_file_dropped_region_deleted、test_missing_last_modified_unknown_kept与test_unknown_file_at_cutoff_not_deleted等用例。同时为便于调试与审计GC 会维护近期已删除文件的完整列表GcReport。源码中GcReport结构体记录了每个 region 删除的数据文件与索引文件、需要下轮重试的 region、以及成功处理的 region 集合src/store-api/src/storage/file.rs。读一致性保障临时 Manifest 与心跳机制为什么需要保护机制GC 最危险的场景是误删仍在被长查询使用的文件。为此RFC 设计了一套依赖临时 manifest 的保护机制创建临时 manifest当检测到长查询例如通过 slow query recorder时查询进程向 region 的 manifest 目录写入一个临时 manifest其中列出该查询所需的全部文件周期性心跳仅仅创建文件不够——如果查询进程崩溃临时 manifest 会变成孤儿文件导致 GC 永远无法回收相关文件。因此执行长查询的进程必须周期性更新临时 manifest 的修改时间戳touch 文件作为查询仍存活的心跳信号GC 校验GC worker 运行时扫描临时 manifest检查其最后修改时间若超过可配置阈值则判定该临时 manifest 已陈旧来自崩溃或已终止的查询将其删除受其保护的文件随即不再受屏蔽动态生命周期临时 manifest 在查询启动时创建、由查询周期性保活、查询正常结束时主动删除、异常终止时由 GC worker 自动清理。仓库中的落地方案FileRefsManifest在 GreptimeDB 当前实现中这一概念落地为FileRefsManifest临时文件引用清单见 src/store-api/src/storage/file.rs包含三部分file_refs每个 region 当前被引用正在被查询使用的文件集合FileRefmanifest_version生成这些临时引用时对应 region 的 manifest 版本用于判断临时引用是否过期cross_region_refs跨 region 文件归属映射记录 repartition 后新 region 持有的、原属于源 region 的文件防止 GC 误删迁移中的文件。GC worker 在do_region_gc()中会做版本一致性校验如果FileRefsManifest中记录的 manifest version 与 region 当前 manifest version 不一致则跳过该 region 的 GCgc.rs并累加GC_ERRORS_TOTAL{manifest_mismatch}指标。这对应 RFC 中读副本的 manifest 版本落后时间不能超过废弃文件的 lingering time否则可能引用已被 GC 删除的文件的约束。两阶段实施建议RFC 同时指出临时 manifest 心跳机制一次性实现复杂度较高建议分两阶段推进Phase 1基于时间的简单删除先实现只依赖可配置 lingering time 的简单 GC 策略为空间回收提供基线能力暂不引入临时 manifestPhase 2一致性感知 GC根据 Phase 1 的实际效果与观测到的问题再决定是否实现完整的临时 manifest 与心跳机制以处理长查询场景。配置参数详解datanode 侧region_engine.mito.gcGC worker 的滞留时间等参数在 datanode 的 mito2 引擎配置中设置完整示例见 config/datanode.example.toml对应GcConfig结构体src/mito2/src/gc.rs配置项类型默认值说明region_engine.mito.gc.enableBoolfalse是否启用 GC。必须与 metasrv 的gc.enable保持一致否则会产生意外行为region_engine.mito.gc.lingering_timeString1h删除文件前的滞留时间。应足够长以让长查询完成设为None时未使用文件会被立即删除region_engine.mito.gc.unknown_file_lingering_timeString1d删除无法确定 expel time 的 unknown 文件前的滞留时间。仅在全量列表 GC 时生效基于对象last_modified时间戳做严格小于比较。生产环境不宜配得过小避免误删 compaction/flush 进行中的 pre-manifest 文件对象存储不提供last_modified时保守保留dropped region 的 unknown 文件立即删除此外源码中GcConfig还包含两个并发控制参数未在示例配置文件中直接暴露使用默认值max_concurrent_lister_per_gc_job默认 32单个 GC job 内并发列表操作的上限max_concurrent_gc_job默认 4datanode 上并发 GC job 的上限防止过多 GC 任务压垮 datanode。这两个参数由GcLimiter通过 tokio 信号量实施每次新建LocalGcWorker时需获取一个信号量许可permit()无可用许可时返回TooManyGcJobs错误gc.rs。metasrv 侧gcGC 调度器何时、对哪些 region 触发 GC由 metasrv 配置控制完整示例见 config/metasrv.example.toml对应GcSchedulerOptionssrc/meta-srv/src/gc/options.rs配置项默认值说明gc.enablefalse是否启用 GC。必须与 datanode 的mito.gc.enable保持一致关闭时 datanode 上的部分文件可能永远不会被删除gc.gc_cooldown_period5m同一 region 两次 GC 之间的冷却时间max_concurrent_tables10并发处理的表数量上限max_retries_per_region3单个 region GC 失败时的最大重试次数region_gc_concurrency16表内 region GC 的并发度retry_backoff_duration5s重试之间的退避时间min_region_size_threshold100MBGC 候选 region 的最小尺寸阈值sst_count_weight0.5GC 打分中 SST 文件数量的权重SST 越多潜在可删文件越多中等优先级file_removed_count_weight1.0GC 打分中待删文件数量的权重可删文件越多优先级越高regions_per_table_threshold20每张表最多选出参与 GC 的 region 数按打分取前 20mailbox_timeout60s与 datanode 的 mailbox 通信超时full_file_listing_interval24h全量文件列表 GC 的执行间隔。全量列表开销大但能清理孤儿文件每 N 个 GC 周期执行一次N 该值 / ticker 间隔tracker_cleanup_interval6h清理 GC tracker 中陈旧 region 条目的间隔仅用于内存清理metasrv 侧 ticker 默认每 5 分钟触发一次 GC tickoptions.rs且所有参数在GcSchedulerOptions::validate()中做了合法性校验例如max_concurrent_tables、retry_backoff_duration等必须大于 0见 options.rs。另外注意gc.experimental_soft_drop软删除表的自动清理为Enterprise Edition 专属功能社区版配置为启用会直接校验失败。已知缺陷与竞态条件RFC 坦诚列出了该设计的固有缺陷仓库实现与调度策略也都围绕这些风险做了针对性设计依赖 Compaction 频率GC 周期与 Compaction 频率直接绑定。Compaction 不频繁的环境中废弃文件会累积较长时间才被回收存储消耗可能增加。为此 metasrv 侧引入了独立的周期性 ticker 调度而非仅靠 compaction 触发缓解了这一依赖与长查询的竞态若长查询已开始但尚未写入临时 manifest而 Compaction 恰好同时开始并将该查询所需文件标记为废弃就可能造成误删。缓解手段是临时 manifest 的写入阈值时间应显著小于废弃文件的 lingering time从而保证下一次 GC 运行时查询已注册引用同时读副本的 manifest 版本落后时间不能超过废弃文件的 lingering time与 region 迁移region-migration的竞态RFC 用一张时序图说明了风险——GC 读取 Region 1 的 manifest 后、Region 迁移将 Leader 切换到 Region 2、Region 2 写入新文件此时 GC 继续扫描目录会发现不在 Region 1 manifest 中的新文件并将其误标为孤儿并删除。根本解法是对同一 region 的 GC 与 region 迁移/重分区操作加互斥锁避免二者并发执行源码层面LocalGcWorker对 region 的 Follower 状态有明确防护run()中若发现 region 处于RegionRoleState::Follower会直接报错拒绝执行 GCgc.rs且 GC 对 manifest version 的一致性校验同样降低了此类风险临时 manifest 上传到对象存储的额外开销会增加复杂度与潜在性能开销但长查询通常不频繁影响预期很小。实现中还通过cross_region_refs处理 repartition 带来的跨 region 文件归属问题进一步降低误删风险与 repartition 的竞态RFC 建议在 GC 期间同时对 region-migration 与 repartition 加锁作为当前阶段的简单可靠方案。结论与权衡RFC 汇总了该集成式 GC 方案的核心权衡方面当前方案集成式 GC实现复杂度中等。需要与 Compaction 流程及 slow query recorder用于临时 manifest 管理做细致集成可靠性高。与 Compaction 集成 利用长查询的临时 manifest显著降低误删风险精确管理废弃文件滞留时间、避免误删新建 SST增强了数据安全性性能开销低到中。GC 在 compaction 之后运行对写入路径的直接冲击最小slow query recorder 管理临时 manifest 的开销对长查询可接受对其他组件的影响中等。需要修改 Compaction 流程以触发 GC、修改 slow query recorder 以管理临时 manifest引入一定耦合但整体提升了数据安全性删除策略基于状态与时间。废弃文件按可配置滞留时间删除若被临时 manifest 引用则暂停未使用文件从未进入 manifest同样受滞留时间约束未决问题与未来工作RFC 留下两个需要进一步讨论与落地的方向Slow Query Recorder 实现细节需要给出 slow query recorder 的具体修改规范以及它与临时 manifest 之间精确的交互机制可配置滞留时间需要为废弃文件与未使用文件分别确立并开放具体的滞留时间配置以在存储回收与数据可用性之间取得最优平衡。替代方案对比方案一独立 GC 服务Standalone GC Service不把 GC worker 集成进 Compaction而是实现一个独立服务周期性基于 manifest 信息与预定义保留策略扫描并清理废弃/未使用文件。优点与 Compaction 解耦可独立扩缩容部署GC 频率与策略可独立于 Compaction 配置缺点需要单独的服务进行管理、监控与组件协调可能与 Compaction 已有的文件扫描逻辑重复保证读一致性需要更复杂的协调机制如分布式锁管理器或更精细的临时 manifest 系统。若集成式 GC worker 被证明不够用或未来需要更高级的 GC 策略可考虑在将来实施此方案。方案二Manifest 驱动的立即删除无滞留时间文件一旦从 manifest 移除就立即删除不设置任何滞留时间。优点GC 逻辑最简单空间即时回收缺点若未做到完全同步极易删除仍被长查询或其他进程使用的文件需要极其健壮且即时的机制来保证无活跃查询引用待删文件可能导致性能瓶颈或复杂的错误处理由于删除的即时性误删问题极难排查调试。总结Global GC Worker 是 GreptimeDB 存储引擎中保障对象存储空间可持续回收的关键设计。它以Compaction 完成为执行边界通过manifest 追踪 滞留时间窗口 临时文件引用清单三层防护在回收 repartition、compaction、manifest 更新失败等场景产生的废弃文件的同时最大程度避免误删活跃查询正在使用的数据。当前仓库中 LocalGcWorker 与 GcScheduler 的协同实现、FileRefsManifest 的跨 region 引用追踪以及 datanode/metasrv 两侧的 GC 配置项共同构成了这一机制的完整工程落地为后续更细粒度的滞留时间调优与 slow query recorder 集成预留了清晰的演进路径。【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表