构建 Apache Iceberg 湖仓架构——维护 Iceberg 数据湖仓

0 阅读43分钟

本章内容包括:

  • 识别并解决由次优 data files 和 metadata files 导致的性能问题
  • 运行 compaction jobs,以优化 file layout 并提升 query speed
  • 管理 snapshot retention,以降低 storage footprint 并满足 compliance needs
  • 使用 Iceberg metadata tables 监控 table health 并指导 maintenance

设计和部署 lakehouse 只是开始。长期价值来自于持续保持平台的高性能、可治理和有韧性。Apache Iceberg 为 data organization、schema evolution 和 transaction isolation 提供了强大能力,但如果没有主动维护,这些优势可能会逐渐削弱。随着 datasets 增长、write patterns 演进、业务需求变化,lakehouse 也必须随之调整。

现在,我们来看一些 operational practices,它们可以让你的 Apache Iceberg lakehouse 长期保持健康和高性能。不同于传统 data warehouses,在那里许多 optimization tasks 由 engine 自动处理,基于 Iceberg 的 lakehouses 会让 engineers 承担更多责任,来管理 data 和 metadata layout。如果缺少主动维护,performance 可能下降,storage costs 可能不必要地增长,governance requirements 也可能更难执行。我们会先识别导致性能退化的常见问题,例如 small files、scattered partitions 和 metadata sprawl,并讨论为什么这些问题在 Iceberg table format 的语境下尤其重要。随后,我们会探索 compaction、snapshot management 和 resource optimization 技术。

到本章结束时,你将获得一个维护 Iceberg lakehouse 的实践框架,帮助你避开陷阱、满足 SLAs,并在不积累技术债的情况下支持持续创新。

10.1 问题:次优 Data Files

即使是经过精心架构的 Iceberg lakehouse,如果 data files 没有被正确管理,也会随时间退化。随着新 records 被摄入、更新或删除,files 的 layout 和 organization 会演进,而且经常以引入低效的方式演进。这些低效不一定会立即导致 failure,但会显著影响 query performance、metadata overhead 和 system stability。

本节概述生产 Iceberg tables 中最常见的 data layout issues。你将了解 small files 如何增加 file I/O,如图 10.1 所示;poorly colocated data 如何导致低效 scans;以及过量 metadata accumulation 如何给 catalog 带来压力。我们还会考察 Merge-on-Read patterns 对 read performance 的影响,因为 delete files 会不断累积。识别这些问题,是维护一个快速、成本高效 lakehouse 的第一步。

image.png

图 10.1:当同一批数据分散在更多 files 中时,需要更多 file I/O operations,从而导致性能变慢。I/O operations 越少,query 完成得越快。

10.1.1 Small Files

Iceberg lakehouse 中最常见的性能陷阱之一,是 small data files 不受控制地累积。这些 small files 通常来自 streaming 或 micro-batch ingestion pipelines,因为它们会以小增量频繁 commit data。虽然 Iceberg 可以正确地、以 transaction 方式处理这些 writes,但长期结果可能是一张 table 由大量小 Parquet files 组成,而不是由较少且大小均衡的 files 组成。可以通过检查 Iceberg metadata tables 识别这种情况,本章后面会讨论这些 metadata tables。例如,查询 files metadata table 可以揭示整体 file count 和 size distribution,从而突出那些 file granularity 正在损害 query efficiency 的 tables。

什么算 “small file”,很大程度上取决于 storage system。在 HDFS-based environments 中,明显小于 HDFS block size 的 files 是低效的。在 cloud object storage 中,情况不同:没有 block size constraint,而且 engines 越来越能容忍较小 files。但即使在 cloud environments 中,过高 file counts 仍然会引入 overhead。核心问题不是 raw I/O throughput,而是 opening files 和 coordinating reads 的累计成本。Query engines 仍然必须 enumerate files、发起 object store API calls,并调度 work,而且这些流程往往部分是串行的。这些固定成本可能在 bandwidth 成为瓶颈之前,就已经主导 query execution time。

除了 execution overhead,small files 还带来一个更基础、更持久的挑战:metadata growth。Iceberg 会跟踪每个 data file,以及对应 statistics、partition information 和 manifests 中的 references。随着 file counts 增加,manifests 和 metadata structures 也会相应增长。这会增加 planning time、metadata scan costs 和 catalog coordination overhead。不同于 execution inefficiencies,metadata growth 不是暂时的;它会随时间累积,必须主动管理。虽然 metadata layout 和 planning 的改进可以让它不那么明显,但底层成本不会消失。

Small files 并非天然错误。Low-latency ingestion pipelines 经常需要 frequent commits 和 fine-grained files,以满足 freshness requirements。问题出在这些 files 被放任不管。Compaction 仍然是 Iceberg lakehouse 中一项关键 maintenance operation,它会合并 files,以控制 metadata growth 并降低 planning overhead,同时保持 correctness 和 data availability。本章后面会更详细探索 compaction strategies 和 tradeoffs。

10.1.2 Poorly Colocated Data

高效 queries 不仅取决于单个 files 的大小,也取决于相关数据在物理上如何分组。当一张 table 的 rows 没有良好 colocated,也就是相关 records 分散在很多 files 中,如图 10.2 所示,query engines 就必须扫描更多 files,并处理更多无关数据,才能取回结果。当 partitioning、sorting 或 clustering strategies 与 query patterns 不匹配时,这种现象非常普遍。

image.png

图 10.2:如果你要搜索所有 “yellow” records,就必须读取全部三个 files,而这些 files 中还包含 query 不需要的数据。

例如,考虑一个按 region partition 的 sales dataset,但它经常按 customer_idproduct_category 查询。因为 partition column 与 filter condition 不匹配,query engines 无法有效 prune irrelevant files。相反,它们必须从多个 partitions 中读取数据,才能定位与特定 customer 或 product 相关的 records。这会增加 file I/O、CPU usage 和整体 query latency。

即使在单个 partition 内,poorly colocated data 也会导致低效。摄入的 records 可能以半随机顺序到达,尤其是在 streaming 或 distributed write scenarios 中,从而缺乏 locality。这会破坏 column-level compression,并削弱 query engine 使用 file 或 row-group statistics 跳过数据的能力。结果很可预期:queries 更慢,成本更高,资源被浪费。

Colocation problems 通常是逐渐出现的。一张 table 起初可能 partition 得很好,但随着 data volumes 和 access patterns 演进,它们可能发生变化。此外,updates 和 deletes 会进一步造成 records fragmentation,尤其是通过 Merge-on-Read(MOR)semantics 执行时,因为 MOR 会 append delete files,而不是重写数据。

修复 colocation issues 需要重新思考 ingestion 期间数据如何分组和组织,以及数据随时间如何 layout。Iceberg 通过 partition evolution 支持这一点,让团队可以随着 data characteristics 和 access patterns 变化,调整 partitioning strategies,而无需重写 historical data。这使得随着 workloads 成熟或 query patterns 演进,引入更合适的 partition schemes 成为可能。

仅靠 partitioning 通常不够。Iceberg 也支持 sort orders,用来定义 records 在 data files 内部的物理排序方式。有效使用 sort orders 时,它们通过将相似 records 聚集在 files 内部来改善 data locality,从而减少 range queries 扫描的数据量,并提升 cache efficiency。Compaction jobs 可以利用这些 sort orders,将许多 small 或 poorly organized files 重写为更少、更结构良好的 files。

除了简单 sorting,人们也越来越关注更高级 clustering techniques,例如 space-filling curves,以优化 multidimensional access patterns。例如,Dremio 为 Iceberg tables 引入了 autoclustering,其工作方式类似于 Databricks 针对 Delta tables 的 liquid clustering。虽然 Iceberg 对这些方法的支持仍在演进,但即将到来的工作,尤其是 Iceberg v4,旨在让更丰富 statistics 和更复杂 clustering strategies 更容易实现。Partition evolution、sort orders 和 compaction 结合起来,构成了一套强大的工具箱,可以改善 data colocation、最小化不必要 scans,并在规模化下维持性能,如图 10.3 所示。

image.png

图 10.3:通过更好的 data colocation,查询所有 “yellow” records 只需要读取一个 file,而不是三个。Partitioning 允许具有唯一值的 records 被写入不同 data files。

关键是让 physical data layout 与 logical access patterns 对齐。当经常一起查询的数据也被存储在一起时,性能收益可能非常显著。监控 query behavior,并相应调整 data organization,是长期 lakehouse maintenance 的核心部分。

10.1.3 Metadata Sprawl

即使 data files 大小合适且 colocated 合理,metadata sprawl 也可能悄悄降低性能。当 metadata layer,尤其是跟踪 data files 的 manifest files,变得碎片化或过大时,就会出现这种情况,如图 10.4 所示。随着时间推移,这会拖慢 query planning,并增加 reads 和 writes 的 coordination overhead。

image.png

图 10.4:当 manifest files 数量超过 data files 数量所需,或 manifests 跟踪过多 small data files 时,就会出现 metadata bloat。两种情况都会延迟 query planning。

要理解 metadata sprawl 如何产生,需要先看 Apache Iceberg 如何组织 table metadata。每张 Iceberg table 都维护一系列 snapshots,每个 snapshot 指向且只指向一个 manifest list。Manifest list 引用一组 manifest files,每个 manifest file 跟踪一组 data files,包括 partition summaries、column-level statistics 和 file paths。在顶层,metadata.json file 记录 table 的 snapshot history、schema versions、partition specifications,以及其他 table-level metadata。

在健康 table 中,metadata growth 与 data growth 成比例。随着新 data files 被添加,新的 manifest entries 会被创建,snapshots 会记录 table 的演进状态。这种增长是预期且必要的。问题不在 metadata 增长本身,而在于它以低效方式增长。High-frequency ingestion jobs,尤其是 streaming 或 near-real-time pipelines,经常创建大量 snapshots,每个 snapshot 只 commit 少量 data files。由于大多数 streaming engines 不会在 writes 期间 merge manifests,每次 commit 通常都会产生新的 manifest files,并在 metadata.json 中产生新的 snapshot entry。

随着时间推移,这会导致两种相关 metadata bloat。第一,manifest files 数量增加,即使每个 manifest 可能只描述少数 data files。第二,更关键的是,记录在 metadata.json 中的 snapshot history 变长。每个 query 都必须读取当前 table metadata 来发现 active snapshot,每个 commit 都必须更新并重写这个 metadata file。随着 snapshot list 变长,即使底层 data layout 依然高效,read 和 write costs 也会增加。

虽然 Iceberg 的 manifest lists 和 manifest files 包含 partition summaries,使 engines 在 query planning 期间可以跳过 irrelevant manifests,但系统仍然会承担 loading 和 traversing 这些 metadata 的开销。这主要影响 query planning latency 和 snapshot management operations。Snapshot expiration 尤其受影响,因为它必须遍历 historical snapshots 和关联 metadata,才能安全移除 unreferenced files。Rollbacks 和 schema evolution 除了读取和写入更大 metadata files 的 baseline cost 外,通常受影响较小。

解决 metadata sprawl 需要显式管理 snapshots 和 manifests。仅仅压缩 data files 并不够。Iceberg 提供 rewrite 和 compact manifests 的机制,减少 fragmentation,并合并描述同一组 data files 的 metadata。同样重要的是 expire old snapshots,以控制 metadata.json 增长,并限制 catalog 必须管理的 historical surface area。Spark 的 RewriteManifests procedure 或 Dremio 的 OPTIMIZE workflows 等工具,可以帮助保持 metadata 紧凑,确保 table evolution 在 ingestion frequency 和规模提升时仍然高效。

图 10.5 展示了更理想的 metadata layout。

image.png

图 10.5:理想情况下,你会有更少 manifest files,跟踪更少 data files,而每个 file 包含更多 records,从而最小化 metadata traversal 和 data file I/O 的成本。

为什么 Iceberg 使用 Manifest-Based Metadata Structure

乍看之下,Iceberg 的分层 metadata structure,也就是 manifest lists 指向 manifest files,manifest files 再指向 data files,似乎会带来不必要的复杂性或 overhead。但这种设计在支持 Iceberg 的关键特性方面发挥核心作用:ACID guarantees、time travel,以及跨 engines 的 compatibility。

Iceberg 中每个 snapshot 都是 immutable 且 fully self-describing 的,这使 rollback、branching,以及 concurrent reads and writes 等 operations 成为可能。为了支持这一点,metadata 必须捕获 table 在每个时间点的完整状态。Iceberg 不是每次都从零重写所有 metadata,而是用描述数据子集的 reusable manifest files 构建 snapshots。这种复用让大多数 workloads 中 metadata growth 可管理,并支持 snapshot-level consistency。

但这种结构也有 tradeoffs。在 frequent writes 或 streaming ingestion workloads 中,即使数据只是 incremental changes,新的 manifests 也可能迅速累积。这会导致 metadata sprawl 和更高 planning costs,这就是为什么 Iceberg 提供 RewriteManifestsRewriteDataFiles 等工具,用于定期清理并合并 data 和 metadata。

Iceberg 社区正在积极探索这一领域的改进。例如,即将到来的 v4 specification 包含关于重构 metadata handling 的 proposals,使 commit 期间只重新创建 metadata tree 中被修改的部分。这些增强旨在降低 commit latency、最小化 catalog writes,并更好支持 high-frequency 和 streaming workloads。

Iceberg 的 manifest structure 体现了一种谨慎平衡:强 transactional guarantees 和开放性是有成本的,但持续创新正在降低这种成本,同时保留其架构优势。

常规 metadata maintenance 必须超越 compaction 本身。重写 data files 和合并 manifests 的确可以改善 query planning 和 scan efficiency,但这些 operations 不会减少 table 的 metadata.json file 增长,也不会移除 obsolete snapshots 和 unreferenced files。如果不主动 expire snapshots,high-frequency ingestion pipelines,尤其是 streaming workloads,会持续积累 snapshot history,直到 metadata file 本身成为瓶颈。

Snapshot expiration 是一项一等 operational requirement,而不是可选 cleanup step。每个新 snapshot 都会向 metadata.json 添加 entries,增加每个 query 和 commit 的 read 与 write costs。如果 old snapshots 从不 expire,这个 file 可能增长到数百 MB,显著拖慢 catalog operations,并在极端情况下导致 ingestion pipelines 停滞或失败。即使 data files 和 manifests 已经良好 compacted,这种风险仍然存在。

有效的 Iceberg maintenance 需要一套协调策略。合理的 ingestion practices 应限制不必要 commits;manifest rewriting 应减少 metadata fragmentation;snapshot expiration 应定期 prune 已不再需要用于 time travel 或 recovery 的 historical state。这些实践结合起来,可以让 metadata growth 与真实业务需求保持比例,确保 dead files 被安全清理,并保持可预测性能。通过将 snapshot expiration 视为核心 lifecycle operation,团队可以随着 data volume 和 ingestion frequency 扩展,保持 lakehouse 敏捷、响应迅速且成本高效。

10.1.4 Merge-on-Read Performance Hits

Merge-on-Read(MOR)是 Apache Iceberg 中的一项强大功能,它通过将 changes 写为 incremental delete files,而不是重写整个 data files,来高效处理 updates 和 deletes。这种方法显著降低 write amplification,并提升 ingestion throughput,尤其适合 high-frequency update workloads。但并非所有 delete files 行为相同,理解它们的差异对管理 read performance 至关重要。

Iceberg 支持两类 delete files:position deletes 和 equality deletes。Position deletes 基于 data file 中的物理位置标识要删除的特定 rows,使 query engines 能在 reads 期间高效过滤 deleted rows。相比之下,equality deletes 基于 column values 定义 deletions,要求 engines 在 query execution 期间根据 data 评估 delete predicates。虽然 equality deletes 更灵活,并且在某些 ingestion pipelines 中更容易产生,但它们通常在 read time 处理成本更高,也更难高效 compact。

随着 delete files 累积,尤其是 equality deletes,read performance 可能下降,因为 queries 必须将 base data files 与多层 delete information 合并。这种取舍是 MOR model 的固有特性。相比之下,Copy-on-Write(COW)在 write time 通过重写受影响 data files 来应用 changes,完全避免 delete files,但会增加 write cost。图 10.6 展示了 MOR 和 COW 的行为差异。选择二者时,需要平衡 write throughput、read performance,以及长期管理 delete files 的 operational cost。

image.png

图 10.6:在 Copy-on-Write 中,当你删除 records 时,包含这些 records 的 data file 会被重写,并排除它们。在 Merge-on-Read 中,不会重写整个 file,而是用 delete file 或 deletion vector 跟踪 deleted records。这可以在读取 table 时进行 reconciliation,使写入 table 更快,但读取变慢。

在 MOR table 中,每次 read operation 都必须扫描 base data files,然后应用所有 relevant delete files,以重建当前数据视图。这种 dynamic merging 增加 query complexity 并拖慢 execution,尤其是当每个 data file 关联的 delete files 数量增长时。在 high-churn workloads 中,例如频繁 upserts 或 time-sensitive updates,这种 overhead 可能成为 read 和 write operations 的主要瓶颈。

NOTE
在 Iceberg v3 中,deletion vectors 会直接与 data files 关联,并且每个 data file 至多有一个 deletion vector,这避免了 delete files 累积导致的无界增长模式。这会将 operational concern 从“delete files 太多”转向维护高效 compaction 和 data layout,因为 read-time overhead 不再由不断增长的 delete artifacts 堆栈驱动。

在 Iceberg v2 tables 中,这个问题会随着时间加剧,因为 delete files 以 append-only 方式写入。较旧的 base data files 可能累积几十甚至数百个关联 delete files。每一个 delete file 都必须在 query execution 期间被读取、解析和应用,增加 read-time overhead,并降低 predicate pushdown 和 data skipping 的有效性。这一行为只适用于 delete files,不适用于 Iceberg v3 deletion vectors,因为后者避免了这种无界累积模式。

一个常见误解是,只要避免 MOR 并切换到 COW,就能消除这些挑战。在 COW mode 中,updates 和 deletes 会重写整个 data files,从而不需要 delete files,并简化 reads。但这会把成本转移到 write operations 上,对于拥有频繁 changes 的 large tables 来说,写入可能变得昂贵。实践中,一些 Iceberg v2 users 采用 hybrid patterns,例如用 MOR 处理 incremental updates,并定期发起 COW-style rewrites,以 compact data 并消除累积 delete files。虽然有效,但这种模式在 Iceberg v3 中大体上已经过时。

在 MOR 和 COW 之间选择,应由 workload characteristics 决定,而不是单纯为了避免 maintenance。实证研究和生产经验表明,update density 存在一个拐点:如果单次 operation 修改了受影响 files 中超过大约 10% 的 rows,COW-style rewrites 通常整体更高效。低于这个阈值时,MOR 往往提供更好 write performance,但代价是增加 read-time complexity。理解 update frequency、row-level change rates 和 query patterns,对为特定 table 和 workload 选择合适 strategy 至关重要。

管理 MOR performance 的正确解决方案是 compaction。Iceberg 支持使用 RewriteDataFiles 等 procedures,将 data 和 delete files 重写为新的、优化后的 base files。Compaction 期间,delete files 会被应用到对应 data files,结果被写成新的 base files,不再需要 dynamic merging。这恢复了 read performance,同时保留 MOR 面向 incoming writes 的效率。

有效 compaction strategies 会同时考虑 delete files 的数量和数据 freshness。例如,你可以选择更频繁 compact 具有高 delete activity 的 partitions,而推迟不活跃区域,以节省资源。Apache Spark 和 Dremio 等工具提供按 partition、file size 或 timestamp 定向 compaction 的机制,使 incremental cleanup 可以在不中断 active workloads 的情况下完成。

MOR 支持可扩展、写入高效的 tables,但必须配套 compaction strategy 来维护 read performance。忽视 compaction 会导致 latency 增长和资源浪费。识别 MOR performance degradation 的信号非常关键,例如 query execution 变慢、query-planning latency 升高、uncompacted delete files 数量增长,或 table size 快速扩大。这些都是维护高性能 Iceberg lakehouse 的关键指标。

10.2 解决方案:Compaction

Compaction 通过将 data 和 metadata 重组为更高效结构,解决 small files、scattered data、fragmented metadata 和 accumulated delete artifacts 等问题。它会重写 Iceberg data files 和 manifest files,以合并它们、降低 overhead,并恢复可预测性能。Compaction 期间,small data files 会被合并成更大的 files;对于使用 delete files 的 tables,delete files 会被应用到对应 base data files;rows 可能会根据定义好的 sort orders 重新排序;多余 manifest files 也会被合并。

结果是更适合 query planning 和 execution 的 layout:需要打开的 files 更少,data colocation 更好,metadata 更容易被 engines 和 catalogs 处理。定期并经过思考地应用 compaction,可以帮助 Iceberg table 在演进过程中保持高性能和可管理性,如图 10.7 所示。

image.png

图 10.7:Compaction 期间,data files 被写成更少、更大的 files,manifests 被重写,使更少 manifests 跟踪更多 files。这会改善经过多次 writes 后退化的 table performance。

虽然 compaction 会提升性能,但也引入 tradeoffs。重写 files 会消耗 compute resources,可能影响 concurrent workloads,并且必须谨慎管理,以与 SLAs 和 throughput constraints 对齐。选择哪些 files 要 compact、输出 files 多大,以及何时运行 compaction jobs,都会影响 lakehouse efficiency。

在下面的 sections 中,我们会探索 compaction 的关键参数:如何确定 target file sizes、如何选择要重写的 files,以及如何使用 filters 和 partition-level targeting 运行 incremental compaction jobs。这些策略共同构成健康、可维护 Iceberg deployment 的基础。

10.2.1 什么是 Compaction?

Compaction 的核心,是一个 rewriting process,用于优化数据的物理存储方式,以提升 read performance 并降低 metadata overhead。Compaction 期间,Apache Iceberg 可以执行多项增强:将 small 或 oversized files 合并为更均衡的 segments、应用更好 compression、对 records 排序以改善 data locality、合并 MOR tables 中的 delete files,并更新 file-level statistics。这些 transformations 减少 query engines 必须扫描的 files 数量、改善 predicate pushdown,并降低 query-planning latency,使 compaction 成为任何 Iceberg lakehouse 的关键 maintenance task。

Iceberg OSS libraries 支持两种主要 compaction procedures:rewrite_data_filesrewrite_manifests。使用带 Iceberg Spark runtime 的 Apache Spark 时,可以使用这些 procedures。其他 engines 也可能实现这些 procedures,例如 Trino;或者提供类似 procedures,例如 Dremio 的 OPTIMIZErewrite_data_files procedure 支持 bin-pack 和 sort 两种 strategies:

Bin-pack 是默认 strategy。它旨在将 small files 合并为 target size 的新 files,避免高成本 file opens 和 metadata overhead。

Sort compaction 更进一步,会基于一个或多个 columns 重新排序 records。这会改善 locality,并加速 filtered queries。例如,按 event_date 排序,或在多个 dimensions 上使用 Z-order,可以提升 pruning effectiveness。

NOTE
Z-ordering 和其他 multidimensional clustering techniques 可以通过跨多个 columns colocating related values,改善 data pruning 和 query performance。不过,这些收益高度依赖 workload,并且有重要 caveats。Z-ordering 可能增加 write 和 compaction costs;如果 query patterns 不一致,它可能效果较差;而且它不一定总能带来有意义的性能收益,尤其是与 aggressive partitioning 或 frequent updates 结合时。它应被视为需要仔细评估的 optimization,而不是默认策略。

每个 compaction procedure 都可以通过一些 options 微调,例如:

target-file-size-bytes——控制 output files 的大小。

min-input-filesmax-file-size-bytes——选择哪些 files 有资格被 rewrite。

where——将 compaction 限制在特定 partitions 或 filtered data subsets。

remove-dangling-deletes——清理 prior updates 中未使用的 delete files。

下面是在 Spark SQL 中对 Iceberg table 执行 bin-pack compaction 的示例:

CALL catalog_name.system.rewrite_data_files('db.sales');

下一个示例使用基于 sort 的 compaction,并使用 Z-ordering:

CALL catalog_name.system.rewrite_data_files(
    table =>'db.sales',
    strategy =>'sort',
    sort_order =>'zorder(customer_id, product_id)'
);

对于使用 Dremio 的 environments,compaction 通过 OPTIMIZE TABLE SQL command 暴露。该命令支持类似 tuning parameters:

OPTIMIZE TABLE demo.example_table
  REWRITE DATA USING BIN_PACK (TARGET_FILE_SIZE_MB = 512);

在两个 engines 中,compaction 也可以包含 metadata optimization:在 Dremio 中追加 REWRITE MANIFESTS,或在 Spark 中调用 rewrite_manifests procedure。虽然 compaction 很重要,但并不总是需要重写所有 files。Selective compaction,例如只 compact 一个被频繁查询的 partition,或低于某个大小的 files,可以实现与 service-level agreement(SLA)requirements 和可用 cluster capacity 对齐的 incremental optimization:

CALL catalog_name.system.rewrite_manifests('db.sales');

定期应用 compaction,可以确保 Iceberg tables 即使在 data volume 和 complexity 增长时,仍然保持轻量、query-friendly 和 resource-efficient。

10.2.2 Target File Size

Compaction planning 中的一个核心决策,是确定 output files 的 target size 和 structure,尤其是这些 files 内部 row groups 的大小。减少 files 数量可以降低 query planning 和 I/O overhead,但 oversized row groups 可能导致 query engines 内存耗尽或 spill to disk,因为 row groups 在 execution 期间不可拆分。Files 本身是 splittable 的,但 row groups 大小不合理会限制 parallelism 并增加 resource pressure。选择合适 file 和 row group sizes,会直接影响 Iceberg tables 的 performance、scalability 和 stability。

Apache Iceberg 的默认 target file size 是 512 MB,通过 write.target-file-size-bytes table property 配置。虽然这可能适合某些 large-scale workloads,但许多 production environments 使用更小 files 效果更好。更小 files 可以降低 compaction 期间 repeated reads 和 writes 的成本,并更好地适应 parallelism 由 row group size 而不是 file size 驱动这一事实。过大的 files 也可能拖慢 compaction,并增加 reads 期间的 memory pressure。选择一个在 metadata overhead 和 efficient rewrites 之间取得平衡的 file size,对实现长期可持续性能非常关键。

但这个默认值不是 one-size-fits-all solution。理想 target file size 取决于多个因素:

Query engine characteristics——有些 engines 比其他 engines 更擅长处理大 files。例如,Dremio 和 Trino 可以高效扫描最大 1 GB 或更大的 files,而其他 engines 可能受益于更小、更适合内存的 files。

Compression——File 在磁盘上的实际大小取决于 compression。Highly compressible data 即使写入大量 records,也可能生成较小 file sizes。

Table width and data shape——包含许多 columns 或 nested structures 的 wide tables,可能受益于略小 files,以避免 query execution 期间内存使用过高。

Sorting——在 executor memory 或 I/O bandwidth 有限的系统上,设置较小 target file size,例如 128–256 MB,可能有助于避免 performance bottlenecks 或 query failures。

你可以在 Spark 中显式设置 compaction 的 target file size,例如:

CALL catalog_name.system.rewrite_data_files(
  table =>'db.sales',
  options =>map('target-file-size-bytes', '268435456')
);

在 Dremio 中,可以使用:

OPTIMIZE TABLE db.sales 
  REWRITE DATA USING BIN_PACK (TARGET_FILE_SIZE_MB = 256);

同样重要的是,要理解这个 target value 是指导性的,而不是严格强制 output sizes。Record boundaries、compression 和 partitioning 等因素可能造成轻微变化。

监控 query engine behavior,例如 parallelism、I/O throughput 和 query execution plans,是验证 compaction strategy 和 file sizes 是否带来真实性能收益的最有效方式。虽然 Iceberg metadata tables 可以显示 files 是否正在收敛到预期大小,但这本身并不能确认 engine 是否从这些变化中受益。相反,审查 Spark、Trino 或 Dremio 等 engines 的 query plans,可以直接洞察 file layout 是否提升了 scan efficiency、减少 spill,并最大化 task parallelism。

实践中,迭代很常见。从类似 10 MB 的默认值开始,评估 query performance 和 storage efficiency,然后根据观察结果和 operational constraints 调整 target。保持一致且有充分理由的 file-size standard,有助于你的 Iceberg lakehouse 随 data volumes 增长而高效扩展。

10.2.3 要包含哪些 Files

并不是每次 compaction job 都需要重写所有 files。不加区分地重写可能浪费资源、增加 job duration,并干扰 concurrent writes。Apache Iceberg 允许通过配置 file size 和 file count thresholds,精细控制哪些 files 有资格参与 compaction。这些参数让 compaction 能聚焦在最需要的地方,在 optimization 和 operational efficiency 之间取得平衡。

Spark 和 Dremio 等 engines 中的 compaction procedures 会使用 file-grouping strategy 来规划 compaction jobs。Engine 不会逐个评估单个 files 是否有潜在收益,而是先枚举所有 eligible files,然后基于 max-file-group-size-bytes 等配置限制,将它们分成 file sets。这些 file groups 成为 compaction 的工作单元,并被独立处理,以提升 parallelism 并控制 metadata growth。

你可以用以下参数影响这些 file groups 如何形成:

min-input-filesMIN_INPUT_FILES——设置某个 group 中必须存在的最少 files 数量,才会被 compact。它主要用于控制 compaction jobs 的 granularity 和 parallelism,而不是评估 compaction 是否必要。真正决定哪些 files 被包含,是在更早的 planning phase 完成的。

max-file-size-bytesmin-file-size-bytes——这些 thresholds 通常用于 file selection phase,判断哪些 files 有资格参与 compaction。例如,过小 files 可能被包含,以减少 file count 和 metadata overhead;而非常大的 files 可能被拆分,以改善 memory efficiency 和 scan parallelism。

rewrite-job-order——该配置控制 file groups 被 compact 的顺序。你可以按最小 files、最大 files,或基于 group size,也就是文件数最少或最多,来排序,具体取决于目标是减少 metadata overhead、改善 I/O balance,还是将 compaction cost 分散到多次运行中。

这些设置为压缩 Iceberg tables 时在 efficiency、throughput 和 operational cost 之间平衡提供了灵活性。

例如,在 Spark 中,你可以使用如下代码:

CALL catalog_name.system.rewrite_data_files(
  table =>'db.sales',
  options =>map(
    'min-file-size-bytes', '134217728',        -- 128 MB
    'max-file-size-bytes', '1073741824',       -- 1 GB
    'min-input-files', '5'
  )
);

在 Dremio 中,对应写法如下:

OPTIMIZE TABLE db.sales
  REWRITE DATA USING BIN_PACK (
    MIN_FILE_SIZE_MB = 128,
    MAX_FILE_SIZE_MB = 1024,
    MIN_INPUT_FILES = 5
  );

在 minimum 和 maximum file sizes 之间选择更窄范围,例如设置相对较高的 min-file-size 和较低的 max-file-size,会通过捕获更多落在 optimal size boundaries 之外的 files,扩大有资格参与 compaction 的 files 池。这增加了综合优化的可能性,但也可能导致更大、更长时间运行的 compaction jobs,消耗更多资源,并可能影响 system availability 或 downstream tasks。

相比之下,更宽范围,也就是 min-file-size 较低、max-file-size 较高,会将许多 files 排除在 compaction 外,只聚焦最小和最大的 outliers。这会减少 runtime 和 resource usage,适合 operational windows 很紧,或 compaction 与 continuous ingestion 并行运行的 environments。不过,这也可能留下部分低效问题未被解决。在 streaming use cases 中取得合适平衡尤其重要,因为 compaction 必须既有效又不中断业务。

为了管理这一点,常见做法是调度 frequent、incremental compaction jobs,并使用较窄 thresholds。这些 jobs 会随时间逐渐把 table 带回 optimized state,而不需要 full-table rewrites。当与 metadata table queries 搭配,用于识别 compaction candidates 时,这种策略就成为一种强大机制,可以在不破坏 SLAs 的情况下维持性能。

最终,哪些 files 应包含在 compaction 中,应由 query patterns、table size、observed query performance 和 available compute resources 等因素指导。Iceberg 提供了根据这些因素定制 compaction 的灵活性,使 lakehouse maintenance 更加定向且高效。

10.2.4 使用 Filters 限定 Compaction 范围

Compaction jobs 可能非常消耗资源,尤其是面对包含数千 files 且分布在许多 partitions 中的大表。正常工作时间或 shared infrastructure 上,一次性重写所有 eligible files 可能不可行。为了解决这一点,Iceberg 支持 filtering mechanisms,允许你根据 partition、time window 或 business logic 定向 compact 特定数据子集。

Spark 和 Dremio 都支持使用类似 SQL 的 predicates 过滤 compaction jobs。这支持 incremental optimization strategies,让 rewrite job 的影响局部化,保持较短 execution time 和可控 compute costs。

例如,在 Spark 中,你可以使用 where 参数定向特定 partition:

CALL catalog_name.system.rewrite_data_files(
  table =>'db.sales',
  where =>"region = 'us-west'");

该命令将 compaction job 限定为只重写 us-west partition 中的 data files,其他 partitions 不受影响。

你也可以使用 compound filters,例如:

where =>"region = 'us-west' AND sale_date >= '2024-01-01'"

Dremio 通过 FOR PARTITIONS clause 提供类似功能:

OPTIMIZE TABLE db.sales
  REWRITE DATA USING BIN_PACK
  FOR PARTITIONS region = 'us-west'

这些 filtering options 在以下场景中特别有用:

  • 你希望将 compaction efforts 聚焦在频繁 writes 或 updates 的 hot partitions 上。
  • 你正在分阶段执行 maintenance,以避免 long-running jobs。
  • 你正在处理 partitioned tables,并且需要在业务关键时段遵守 system SLAs。

Filtering 也支持 adaptive compaction。例如,如果 monitoring 显示某个 partition 出现大量 small files 或 accumulated delete files,你可以触发只针对该 segment 的 compaction job。这种方法与依赖 metadata tables 或 external monitoring systems 检测 table health signals 并触发 maintenance workflows 的 automation strategies 非常契合。

Scoped compaction 的一个关键优势,是可以根据每张 table 的具体特征定制 optimization strategies。不是对整个 lakehouse 应用 one-size-fits-all process,而是可以定义反映单个 workloads 需求的 compaction logic。例如,一张 table 可能受益于按 regional dimension 排序以改善 query pruning,而另一张 table 可能只需要 file consolidation,不需要 sorting。这种灵活性使 compaction 更贴近 data access patterns,在最重要的地方提升性能。

当与 size-based file inclusion thresholds 结合时,filters 会赋予你对 compaction 的 fine-grained control。你不必无差别地重写整张 table,而是可以在最重要的位置迭代性地维护性能,使 Iceberg lakehouse 在不牺牲稳定性或效率的情况下保持优化。

10.3 Storage Footprint Management 和 Data Retention

Apache Iceberg 对 time travel 和 snapshot isolation 的支持,是其最强大的功能之一,它允许用户查询 historical table states,并回滚不需要的 changes,如图 10.8 所示。但这种能力有一个取舍:data 和 metadata 会跨 snapshots 保留,从而随时间增加 table 的 storage footprint。那些已经被逻辑删除的 files,可能仍然物理存在,直到被显式清理。

image.png

图 10.8:Iceberg tables 具有 snapshot isolation,允许你查询 table 的旧版本。代价是所有 historical data 仍然与 current data 一起存储。

本节解释 Iceberg 如何处理 data retention、有哪些机制可以移除不再需要的 files,以及如何在 regulatory compliance 和 cost efficiency 之间取得平衡。如果不定期 cleanup,old snapshots 和 unreferenced files 的累积可能导致 storage costs 上升,并降低 table performance。

我们会先考察 snapshot expiration,这是 prune obsolete data 和 metadata 的主要机制。然后探索所选 write mode,也就是 Copy-on-Write(COW)或 Merge-on-Read(MOR),如何影响 file lifetimes 和 deletion behavior。最后,我们会回顾 regulatory compliance 方面的考虑,包括 GDPR 和其他 data retention mandates,这些规则要求永久移除用户数据。

10.3.1 运行 Snapshot Expiration

为了支持 time travel 和 snapshot isolation,Apache Iceberg 会在 table operations 中保留 metadata 和关联 data files 的完整 lineage。随着时间推移,这些 historical snapshots 会累积,消耗 storage space 和 metadata-processing time。虽然这些 snapshots 对 rollback 和 auditing 有用,但最终必须被 prune,以维持健康的 lakehouse environment。这就是 snapshot expiration 发挥作用的地方。

Snapshot expiration 会移除 old table snapshots,以及只与这些 snapshots 关联的 data 和 metadata files,如图 10.9 所示。关键的是,Iceberg 会确保任何 active 或 retained snapshot 所需的 file 都不会被删除。这保证了即使 history 被裁剪,queries 仍然可靠且可复现。

image.png

图 10.9:当 Snapshot 1 过期时,只有不与剩余 snapshots 关联的 metadata 或 data files 会被删除。

Spark 中的 Snapshot Expiration

在 Spark 中,expire_snapshots stored procedure 处理这项任务。你可以使用各种参数控制 snapshots 过期的激进程度。最常见的使用模式是设置时间边界,并保留最少数量的 recent snapshots:

CALL catalog_name.system.expire_snapshots(
  table =>'db.sales',
  older_than =>TIMESTAMP '2024-06-01 00:00:00.000',
  retain_last =>20
);

该调用会移除所有早于 2024 年 6 月 1 日的 snapshots,同时确保至少保留最近 20 个 snapshots。对于生产系统,通常会配置该 procedure 定期运行,既可以基于 time windows,也可以由 metadata table monitoring 触发。

还有几个额外 options:

snapshot_ids——直接 expire 特定 snapshots。

clean_expired_metadata——移除 unreferenced partition specs 和 schemas。

max_concurrent_deletes——并行化 delete operations,以加快 cleanup。

stream_results——流式返回 deletion results,避免 Spark driver memory issues。

这些 options 提供了 operational flexibility,并使 snapshot expiration 可以随 table size 和 system capacity 扩展。

Dremio 中的 Snapshot Expiration

在 Dremio 中,snapshot expiration 通过 VACUUM TABLE command 执行:

VACUUM TABLE s3.sales EXPIRE SNAPSHOTS older_than '2024-06-01 00:00:00.000' retain_last 20;

这与 Spark procedure 具有相同效果:移除早于某个具体日期的 snapshots,同时保留基准数量的 recent snapshots。如果没有提供参数,Dremio 使用默认策略,即保留最后一个 snapshot,并移除早于 5 天的 snapshots。

这些 operations 的结果包含 deleted data files、delete files、manifest files 和 statistics files 的数量,帮助团队理解 expiration 对 storage usage 的影响。

Considerations

Snapshot expiration 对 active table state 是安全且非破坏性的,但它定义了一个边界:超过这个边界的 historical table versions 将不再可访问。一旦某个 snapshot 被 expired,就无法再 time travel 到那个时间点。因此,必须将 expiration schedules 与组织的 auditing、compliance 和 recovery policies 对齐。

Snapshot expiration 也会影响 long-running queries。Query 会绑定到它开始时的 current snapshot。如果该 snapshot 在 query 仍在运行时被 expired,query 可能失败。因此,expiration intervals 不仅要考虑 governance requirements,也要考虑 workload behavior。例如,如果 streaming table 每小时 expire snapshots,那么针对该 table 的任何 query 都不能安全运行超过一小时。必须谨慎协调 expiration policies 和 query execution characteristics,以避免意外中断。

在 high transaction volumes 的 environments 中,frequent expiration 有助于防止 storage bloat,并保持 metadata operations 快速。对于变化较慢的 tables,expiration 可以不那么频繁,但仍应被纳入常规 maintenance task。与 orphan file removal,也就是后面会描述的操作,结合使用时,snapshot expiration 是维护 cost-efficient 和 performant Iceberg lakehouse 的关键步骤。

Near-Real-Time 和 CDC Ingestion 的 Snapshot Retention Strategies

Change data capture(CDC)和其他 near-real-time ingestion patterns 会为 Iceberg maintenance 带来独特挑战。由于这些 pipelines 会频繁 commit 新数据,有时一天数千次,table 的 snapshot history 可能增长极快。每次 commit 都会生成一个新 snapshot,如果不干预,就会造成 metadata 膨胀、增加 query-planning times,并给 catalog performance 带来压力。

为了管理这一点,组织通常需要比 batch 或 micro-batch workloads 更激进地 expire snapshots。虽然 aggressive expiration 可以降低 storage 和 metadata overhead,但它也会最小化 time travel 的收益,因为保留的 historical states 更少。此外,retain_last 这类保留固定数量 recent snapshots 的配置,在 high-frequency scenarios 中可能效果较差,因为即使少量 snapshots,也可能只代表几分钟或几小时的历史。

在这些情况下,snapshot retention policies 必须围绕真实 workload behavior 调优,而不是只依赖固定 snapshot counts。Time-based expiration windows,例如只保留最近 2 小时的 snapshots,通常更适合 high-frequency ingestion tables。但当某些 historical states 必须被保留,例如用于 audits、backfills 或 controlled rollbacks 时,snapshot tagging 提供了更精确的机制。

通过标记特定 snapshots,团队可以保护重要 table states 不被 expiration,同时仍激进 prune 常规 snapshot history。该方法允许控制 metadata growth,同时不牺牲关键 recovery points。正确平衡取决于 workload characteristics 和 organizational requirements,但使用 tags 显式保留有意义的 snapshots,通常比不加区分地保留大量 snapshots 更稳健,尤其适用于 CDC-heavy 或 streaming-oriented tables。

10.3.2 COW vs. MOR:对 Data Retention 的影响

Apache Iceberg 支持两种主要 write modes:COW 和 MOR。虽然两者都支持 ACID operations 和 snapshot isolation,但它们在 updates 和 deletes 如何物理应用到数据上有显著差异。这些差异会直接影响 file retention 和 storage cleanup strategies。

Copy-on-Write

COW 是 Apache Iceberg 的默认 write mode。在这个模型中,update 和 delete operations 会创建新的 data files,这些新 files 反映所需 changes,例如移除或修改 rows。Engine 不会重写 existing files,而是写入应用 changes 后的新 files。随后 table metadata 会被更新,以在新 snapshot 中引用这些新 files,而原始 files 仍被 previous snapshot 引用,以保留 time travel capabilities。

因为 deletions 在 write 期间被物理应用,COW tables 不会像 MOR tables 那样积累单独的 delete files。相反,它们会积累 replaced data files:一旦引用这些 files 的 snapshots 过期,它们就不再需要。这种 data files 和 manifests 的紧密耦合,确保只有被 metadata 主动引用的 files 被保留。

例如,COW 中的一次 delete operation 会:

  1. 创建一个新 data file,并省略 deleted rows。
  2. 在新的 manifest 中记录该 file,并由最新 snapshot 引用。
  3. 让原始 file 继续由 previous snapshot 引用,直到它被显式 expire。

这种 eager materialization 简化了 retention management。当 operations 通过重写 data files,而不是依赖 delete artifacts 来实现时,生成的 snapshots 会完整封装当时的 table state。一旦旧 snapshots 过期,任何不再被引用的 data files 就有资格进行 physical cleanup。这使 storage management 更可预测,因为 file lifetimes 与 snapshot retention 紧密绑定,而不是与 auxiliary delete artifacts 的累积绑定。

Merge-on-Read

MOR operations 最适合只修改少量 existing data 的 workloads,此时最小化 write amplification 比立即重组 data files 更重要。常见例子包括 GDPR delete requests、少量 records 的 corrections,或针对少量 entities,例如 sensors 或 user profiles,进行 incremental updates。对于这些 changes,MOR-style operations 不会重写整个 data files,而是单独记录 updates,并推迟重组。

在 Iceberg v2 tables 中,这些 changes 使用 delete files 捕获,既可以是 position deletes,也就是标记 data file 中的特定 row positions;也可以是 equality deletes,也就是根据 column values 识别要删除的 rows。在 query time,engines 会动态应用这些 deletes 以过滤受影响 rows。在 Iceberg v3 中,类似 workflows 可以通过 deletion vectors 实现。Deletion vectors 会将 delete information 直接与 data files 关联,并避免 delete artifacts 的无界累积。跨两个版本,意图一致:高效处理 sparse、incremental changes,同时将更重的 rewrite work 推迟到 compaction 或 maintenance operations。

这种设计提升 write throughput,但有重要 tradeoffs。随着时间推移,需要扫描和维护的 metadata 量可能增长,尤其是 delete artifacts 在许多 snapshots 中累积时。Position deletes 和 equality deletes 都会促成这一点,但 equality deletes 往往带来更严重的长期挑战。因为 equality deletes 会在 read time 根据 column values 被评估,它们处理成本更高,也比基于 position 的方式更难高效 compact。

在 Iceberg v2 tables 中,base data file 不能被物理移除,直到所有引用任何关联 delete artifacts、position deletes 或 equality deletes 的 snapshots 都过期。Equality deletes 特别容易跨 snapshots 复用,这可能无意中延长本已 obsolete 的 data files 的寿命。即使大多数 historical snapshots 已经过期,单个 long-lived equality delete 也可能阻止 cleanup。这种 retention coupling 是 Iceberg v3 引入 deletion vectors 的原因之一,因为 deletion vectors 会将 delete state 局部化到单个 data files,并避免 delete files 造成的无界累积模式。

例如,如果 snapshot 103 中引入的 position delete file 仍被 snapshot 110 引用,那么 expire snapshots 101 到 105 并不会触发 base file 或 delete file 的 cleanup。这可能导致 storage usage 增长,即使 snapshot expiration 正在执行。

为了缓解这一点,Iceberg 提供 rewrite_position_delete_files procedure,可以 compact 并重组 delete files。该过程有助于消除 dangling deletes,也就是不再匹配任何 live data files 的 delete files,从而降低 storage overhead 并改善 planning performance。

NOTE
截至 Iceberg v3 specification,delete vectors 预计将取代 position deletes,提供更高效 tracking,并与现代 query engines 更好集成。这些改进会简化 retention,并降低 MOR tables 的长期 storage footprint。

Operational Guidance

有效的 snapshot 和 file retention strategies 在很大程度上取决于 workload characteristics 和 write behavior。实践中,许多 operational issues 并不是来自某一个单独功能,而是来自 ingestion、compaction 和 retention 并发运行时的相互作用。以下指导聚焦于安全且可预测地管理这些相互作用:

让 snapshot expiration 与 query behavior 和 write frequency 对齐。

定期 snapshot expiration 对控制 metadata growth 至关重要,但 expiration intervals 必须反映 workload realities。对于主要由 rewrite-style operations 构成的 workloads,每天 expire snapshots 通常足够。对于接收频繁 incremental updates 的 high-change tables,通常需要更激进 expiration。

Snapshot expiration 有一项重要 operational constraint:query 不能运行超过它开始时 snapshot 的 retention window。如果 snapshots 每小时 expire,针对该 table 的任何 query 都必须在一小时内完成,否则可能失败。因此,必须让 expiration policies 不仅与 ingestion rates 对齐,也与 query runtimes 和 operational SLAs 对齐。

Snapshot expiration 单独使用并不会减少 data 或 metadata size,除非它与 compaction 结合。Expiring snapshots 会移除 historical references,但只有当重写后的 files 不再被引用时,file cleanup 和 metadata consolidation 才会发生。

管理 compaction frequency,并避免 write collisions。

Compaction 对控制 delete artifacts、file counts 和 metadata growth 是必要的,但必须与 ingestion 谨慎协调。最常见的 operational pitfalls 之一,是 compaction jobs 与 active writers 冲突,两者同时试图重写重叠数据。

为了缓解这一点:

  • 优先 compact closed 或 stable partitions,例如不再接收 writes 的 time-based partitions。
  • 除非 engine 支持安全协调,否则避免对正在 active ingestion 的 partitions 运行 compaction。
  • 尽可能在可预测的 low-write windows 中调度 compaction。

对于 high-change tables,compaction 可能需要频繁运行,但频率应随 write cadence 调整。只有当 table 每几十秒接收一次新 commits 时,每 5 分钟运行一次 compaction 才有意义。否则,compaction jobs 可能增加 overhead,却没有明显收益。

监控 table-level 和 partition-level trends,而不只是 file counts。

Operational monitoring 应聚焦 aggregate trends,而不是 isolated metrics。Delete artifacts 和 file counts 会在 compaction cycles 之间自然增长,并在 compaction 后下降。健康系统通常呈现 sawtooth pattern,也就是 metadata 和 file counts 以可预测方式上升和下降。

Warning signs 包括:

  • Delete artifacts 只增长、不下降。
  • Snapshot counts 增长快于预期。
  • 即使 data volume 稳定,metadata files 仍变大。

这些 patterns 通常表明 compaction schedules 不对齐、snapshot expiration 太保守,或 ingestion commit 过于频繁。

有意识且谨慎地使用 snapshot tags。

Snapshot tags 和 branches 是用于 reproducibility、auditing 和 controlled rollback 的强大工具,但它们会直接影响 retention。任何受 tag 或 branch 保护的 snapshot 都不会被 expiration,这可能阻止 data files 和 metadata 被清理。

对于 fast-moving tables,最佳实践是:

  • 在 major compaction events 之后应用 tags,使保留 snapshots 引用 optimized layouts。
  • 避免标记包含大量 small files 或 delete artifacts 的 snapshots。
  • 只为 audits 或 backfills 等明确定义的用途选择性使用 tags,而不是把 tags 当作一般 retention mechanism。

由于 tags 和 branches 是 retention anchors,应将其视为 operational lifecycle 的一部分,而不是被动 metadata。

将 retention、compaction 和 ingestion 视为一个协调系统。

Snapshot expiration、compaction 和 ingestion 不能独立调优。Snapshot expiration 限制 metadata growth,但会约束 query runtimes。Compaction 改善 read efficiency,但可能与 active writes 冲突。Ingestion frequency 同时驱动 snapshot growth 和 delete artifact accumulation。

维护高性能 Iceberg lakehouse,需要基于 write rates、query behavior 和 retention requirements 协调这三者。当它们被一起调优时,这些机制可以让 tables 在持续 high-ingestion workloads 下仍保持高效、可预测且成本高效。

10.3.3 Data Deletion 的 Regulatory Considerations

随着 data volumes 增长,处理 personal 和 sensitive information 的法律义务也在增加。General Data Protection Regulation(GDPR)、California Consumer Privacy Act(CCPA)等法规,要求组织确保某些 user data 可以按请求永久删除。这在 Apache Iceberg 这类系统中带来独特挑战,因为 Iceberg 的设计是为了保留 data history,以支持 snapshot isolation 和 time travel。

在 Iceberg 中,delete operations 不会立即从 storage 中移除 files。相反,它们会更新 table metadata,使某些 records 或 files 从 future snapshots 中排除。数据的物理移除只有在相关 snapshots 过期且 data files 被 compact 后才会发生。这种 deferred deletion model 虽然有利于 performance 和 auditability,但会让那些要求及时且不可逆擦除的法规合规变得更复杂。

让 Snapshot Expiration 与 Regulatory Requirements 对齐

为了遵守 GDPR 或 CCPA 等 data deletion regulations,仅仅将数据逻辑标记为 deleted 是不够的;你还必须确保对这些数据的所有 physical references 都被移除。在 Apache Iceberg 中,这需要一个多步骤方法:

  1. Expire 引用 data subject records 的 snapshots。
    Snapshots 会保留对 data files 的引用,因此只要某个 snapshot 存在,它引用的数据就仍然可访问。
  2. 频繁 compact,尤其是在 MOR tables 中。
    MOR mode 中的 delete files 包含指向 base data files 中 rows 的 pointers,而不是数据本身,因此需要 compaction 物理重写这些 base files,并排除 deleted rows。
  3. 确保没有 retained snapshots 仍引用 old files。
    即使 compaction 重写了某个 file,原始版本也不会被删除,直到所有引用它的 snapshots 都过期。
  4. 在 expiration 和 compaction 后运行 orphan file cleanup,从 storage 中移除 unreferenced files。

需要理解的是,Iceberg snapshots 不会按逻辑分区数据。例如,一个 snapshot 并不会只包含 aggregates,而另一个 snapshot 只包含 personal data。所有 snapshots 都表示同一张 table 的一致视图,并引用 shared data files,除非有 changes 被 committed。因此,移除 personal data 意味着必须识别并 expire 每一个引用原始 files 的 snapshot,而不只是发生 changes 的那些 snapshots。

Merge-on-Read Tables 的 Considerations

在 MOR tables 中,delete operations 会生成单独 delete files,而不是直接修改底层 data files。这些 delete files 可能跨许多 snapshots 持续存在,使 deleted records 比预期更“持久”,除非运行 cleanup procedures。为了确保监管合规,尤其是处理 data deletion requests 时,必须执行以下操作:

  • 运行 Iceberg 内置 procedures,例如 rewrite_position_delete_filesexpire_snapshots,以移除 obsolete delete records 及其 references。
  • 确保 compaction 已经重写 base files,使 deleted rows 被物理移除,而不只是 query time 被逻辑过滤。
  • 确认没有 retained data files 仍包含 deleted rows,并且所有相关 snapshots 都已经 expired。

这些步骤由 Iceberg 的 maintenance procedures 自动处理,但用户必须在合适 intervals 运行它们。定期运行这些操作,对于确保 deletions 在逻辑和物理层面都完整且合规至关重要。

Branches、Tags 和 Snapshot Retention

Iceberg 中的 branches 和 tags 允许用户保留 table 的 alternate views,例如用于 audit 或 rollback。但被 branches 或 tags 引用的 snapshots 默认不会 expire。这可能无意中让数据保留时间超过预期 retention period。

为了确保合规:

  • 定期审计和管理 custom branches 与 tags。
  • 在合适场景下,使用 history.expire.max-ref-age-ms property,为 branches 和 tags 设置 automatic expiration policies。

Recommended Practices

  • 自动化包含 regulated data 的 snapshots 的 expiration workflows。
  • 避免对与 personal 或 sensitive information 相关的数据使用 long-lived branches。
  • 将 data subject management systems 与 data lakehouse tooling 集成,以编排 metadata 和 storage 层面的 deletions。
  • 监控并记录 expiration events,以便在 audits 期间证明合规。

通过围绕最严格 regulatory requirements 设计 retention 和 deletion strategies,团队可以确保 Iceberg 的 time travel 和 rollback capabilities 永远不会与 privacy obligations 冲突。实践中,许多组织采用保守方法,以满足最严格 GDPR-style deletion timelines 的 cadence 运行 snapshot expiration、compaction 和 file cleanup。这保证 personal data 在规定窗口内被完全移除,同时在 compliance 允许的地方,仍然保留对 recent states 的 controlled time travel。

10.4 探索 Apache Iceberg 的 Metadata Tables

Apache Iceberg 捕获详细 metadata,以支持其 transactional model、schema evolution、partitioning 和 snapshot-based time travel。除了服务内部 table mechanics,这些 metadata 也通过一组 metadata tables 暴露给用户。这些特殊 tables 提供了 Iceberg table 内部状态的透明视图,并可通过 Spark、Trino、Flink 和 Dremio 等 engines 中的标准 SQL queries 访问。

Iceberg metadata tables 覆盖一系列 operational dimensions,可用于更好理解你的 tables:

File-level insights——了解 data files 如何分布在 partitions 中、它们的大小,以及相关 statistics。

Manifest and snapshot visibility——检查 data files 如何在 snapshots 中分组和 versioned。

Delete file tracking——监控 MOR tables 中 delete files 的累积,并识别何时需要 compaction。

Partition evolution and layout——审计 partition structures,并检测 fragmentation 或 skew。

History and snapshot lineage——跟踪数据随时间如何以及何时变化,支持 governance、auditing 和 rollback。

这些 metadata tables 是主动 table maintenance 的强大工具。它们允许 engineers 检测 small file accumulation、跟踪 snapshot growth、识别未优化 partitions,并触发 compaction 或 snapshot expiration 等纠正动作。通过将 metadata table queries 集成进 monitoring pipelines 或 scheduled jobs,你可以自动化大部分维护高效 lakehouse 所需的 operational upkeep。

关于所有 Iceberg metadata tables 的完整参考,包括 schemas 和使用示例,请参见附录 A。

下一章中,我们会走出核心平台特性,探索 Iceberg 不断增长的 Python ecosystem,以及其他完善现代 Iceberg deployment 所需的 operational 和 architectural considerations。

小结

Iceberg 通过结构化、相互连接的 metadata files 管理 physical files,因此 maintenance 是一项关键 operation。

Compaction 对管理 metadata overhead、合并 changes、移除 deleted rows 和改善 read performance 至关重要,尤其是在 Merge-on-Read(MOR)tables 中。

Snapshot expiration 和 garbage collection 可以防止 metadata 和 file storage 无界增长。

有效的 file-sizing 和 clustering strategies 有助于优化 query performance,并降低 scan costs。