Apache Iceberg V3 行级血缘:_row_id 机制、并发写入的行 ID 继承分配、equality delete 断链与跨引擎可读性
Apache Iceberg V3 是 V2 引入行级删除和 sequence number 之后最重要的一次规范更新。对批量分析团队,它是效率提升:deletion vectors 比 positional delete 快、default column values 免去回填、row lineage 简化 CDC。对流式数据平台团队,V3 更根本——它是第一个认真对待流式治理的版本。V2 治理是静态的:管道跑完、验证输出、更新 lineage 元数据,节奏慢到人可以盯着。流式把这一整套假设打碎:数据持续到达、schema 无预警演化、血缘不再是静态 DAG,而是「哪些记录、从哪些源、过哪些转换、在什么时间点、产生哪些输出行」的连续演化图。
两个强制隐藏字段
行级血缘是 V3 在治理领域的旗舰特性,规范强制每张 V3 表为每行跟踪两个隐藏元数据字段:
_row_id:行首次写入表时分配的唯一 long 标识,永久存在。它扛过 compaction、文件重写和元数据更新;同一行无论落在哪个物理文件,_row_id都不变。_last_updated_sequence_number:最后一次改动该行的提交的 Iceberg sequence number。它直接映射到某个 snapshot,而 snapshot 又映射到时间戳、job 和一批 manifest 变更。
这两个字段非可选,V3 强制对所有新建行跟踪。引擎必须维护 next-row-id 表字段,并通过继承方式分配 ID:行首次加入时,其 _row_id 在数据文件里置为 null,读时由文件的 first_row_id 区间分配。这种继承模型让 writer 无需同步协调即可分配唯一 ID——对多个 Flink subtask 并发提交的高吞吐流写尤其关键。
流式管线需要的是什么
在 V3 之前,流式场景做行级血缘要依赖外部基础设施:自建血缘库、在数据模型里嵌入自定义元数据列,或用引擎私有扩展,每个都有代价。自定义元数据列撑爆数据模型,还要求每个生产者正确填充——Flink job、Spark job、CDC connector 各维护一份,任何一环漏填链就断。外部血缘库(Atlas/DataHub/OpenLineage)只能做到 job/数据集级别,能说「Job X 在时间 T 写了表 Y」,说不出哪些输入记录产生了哪些输出行。引擎私有扩展(如 Spark 内部行跟踪)别的引擎读不了。
V3 行级血缘是格式级的,直接写进 Parquet 文件本身。任何能读 Iceberg V3 的引擎——Spark、Flink、Trino、Dremio、Snowflake——无需额外配置就能查血缘列。这个跨引擎可移植性,是它能用在多引擎流平台上的前提。
由行级血缘获得的治理能力
典型管道是 PostgreSQL → Debezium → Kafka → Flink → Iceberg。落进 V3 表的每行都带永久 _row_id 和记录最后修改的 _last_updated_sequence_number。行被更新时(比如客户改地址),_row_id 不变,身份持续;_last_updated_sequence_number 前进到新提交的序号。由此得到:
- 增量变更检测免全表扫描:下游消费方查
_last_updated_sequence_number > X,找出自上次 checkpoint 后改过的所有行,替代 V2 CDC 昂贵的 snapshot-diff。 - 被遗忘权合规:GDPR 删除请求到达时,通过
_row_id沿转换链追踪所有从某源记录派生的行,前提是转换层保留或映射了行 ID。 - 审计轨迹无需外部基建:
_row_id和_last_updated_sequence_number对 snapshot 元数据表 join,即可得到完整审计轨迹。 - 流式去重:Flink 从较早 Kafka offset 重放时,行 ID 提供表级检测和解决重复的机制。
行 ID 分配机制
每个 snapshot 维护 first-row-id 字段。writer 建新数据文件时不把行 ID 直接写进 Parquet 列,而是写 null;读时引擎用文件从 manifest 继承的 first_row_id,加上文件内行位置组合出行 ID。这个设计有几层含义:
- 并发 writer 零协调:多个 Flink subtask 可同时写数据文件,不需要分布式序列计数器。行 ID 区间在 commit 时分配,而非写时分配。
- compaction 保留血缘:小块重写成大块时必须保留原始
_row_id值。行身份绑定的是逻辑含义,不是物理位置。 - 乐观并发可用:Iceberg 的乐观提交模型(writer 各自准备、commit 时解决冲突)与行血缘完全兼容,因为 ID 分配发生在 commit 阶段。
equality delete 的断链坑
一个重要 caveat:行血缘不跟踪经 equality delete 更新的行。用 equality delete 的引擎会避免在改动前读已有数据,无法为新行提供原始行 ID,这类更新会被当作「旧行完全删除 + 新行加入」。重度依赖 equality delete 的流管道(部分 CDC 模式)在这些变更点会断掉血缘连续性。V3 同时引入的 deletion vectors 提供了一条绕过此限制的路径。