1. 概要
在数据湖表格式的演进中,Apache Iceberg 和 Apache Paimon 走出了两条截然不同但又殊途同归的路线:
- Apache Iceberg(开放数据湖标准底座) :以打造绝对开放、多引擎互操作的通用标准为核心。随着 Iceberg v3 的成熟,其全面引入了 Binary Deletion Vectors(删除向量)、Row Lineage(行级血缘)、VARIANT(半结构化数据)等特性,彻底补齐了在行级更新、CDC 增量读和 Schema 演进上的短板,面向“全能型开放湖仓”迈进。
- Apache Paimon(流批一体流式湖仓) :诞生起就带着强烈的“流处理”基因。通过将 LSM-Tree(日志结构合并树)架构引入数据湖,它在设计上将 Flink 流式写入、变更流(Changelog)生成、低成本 Upsert 作为一等公民,是目前处理 CDC 入湖和近实时消费的最优工程化解法。
这里先给出一个初步的结论,后面我会从各个维度及业务场景方面进行对比
追求 “多引擎、跨云生态、极致的开放标准” ,首选 Iceberg;
追求 “Flink CDC 高频入湖、近实时消费、省心的流式链路” ,首选 Paimon。
2. 核心架构与定位解析
2.1 Iceberg:基于不可变文件的元数据管理艺术
Iceberg 是一个面向海量分布式存储的表格式规范。它的核心思想是用一套严格的 Metadata + Snapshots + Manifests 体系来管理底层的不可变数据文件(Parquet/ORC)。通过乐观并发控制(Optimistic Concurrency)原子性地替换元数据指针,它实现了极强的隔离级别、Time Travel 和 Schema 演进。Iceberg 更像是一个“数据库内核的存储管理层”,强调规范和引擎解耦。
2.2 Paimon:湖上的 LSM 树与流式队列
Paimon 被定位为“Streaming Lakehouse Table Format”。在主键表(Primary Key Table)场景下,它在数据湖的文件系统之上构建了一套类似 LSM 树的结构。这使得它不仅能像常规数据湖一样批量读取,还能像消息队列(如 Kafka)一样被流式消费(生成 Changelog),天然解决了高并发行级变更的合并与读写放大问题。
3. 核心维度深度对比
3.1 写入模型与行级更新(Upsert / Delete)
这是两者底层架构差异最大的地方。
- Paimon(LSM 原生 Upsert) :主键表采用 LSM 树架构,写入时数据先进入内存缓冲并追加写入,随后在后台通过 Compaction 进行分层合并。这种机制对高频 Upsert/Delete 极度友好,几乎没有随机写瓶颈,写入吞吐量极高。
- Iceberg(MoR + Deletion Vectors) :Iceberg v2 的 MoR(Merge-on-Read)机制在频繁更新时会产生大量 Positional Delete 文件,导致严重的“读放大”(查询时需逐一匹配过滤)。Iceberg v3 是一个转折点,它引入了基于 Roaring Bitmap 的 Binary Deletion Vectors (DV) 。DV 将删除信息压缩到位图并附着在数据文件旁(通常为
.puffin格式),从机制上压制了小文件地狱,极大地提升了频繁更新场景下的读取性能。
3.2 CDC 与变更数据消费
- Paimon(原生 Changelog) :Paimon 在 Compaction 过程中可以自动生成完整的 Changelog(包含
-U,+U,I,D消息)。这意味着下游的 Flink 任务可以直接将 Paimon 表作为流式 Source,无缝接力消费,工程闭环非常完善。 - Iceberg(Row Lineage 增强) :过去,Iceberg 找增量变更往往依赖 Snapshot 差异或外部 Watermark。v3 规范引入了 Row Lineage,每行数据携带全局唯一的 Row ID 与最后提交的 Sequence Number。这使得“查找增量变更”变成了极低成本的元数据过滤,让 CDC 提取更加接近湖仓的原生属性。
3.3 半结构化数据与 Schema 演进
- Paimon:支持常规的 Schema Evolution(加减列),但对于复杂的半结构化嵌套数据,更多依赖于计算引擎本身的处理能力。
- Iceberg v3(数仓体验降维打击) :v3 在规范层面引入了 VARIANT 原生半结构化类型,允许 JSON 等动态 Schema 数据直接入湖并支持谓词下推,无需提前展平宽表。同时,v3 引入了 Default values(initial-default / write-default),给 PB 级大表加列并设置默认值变成了纯粹的元数据操作,彻底免除了重写数据的昂贵成本。
3.4 生态与多引擎互操作
- Paimon:以 Flink 生态为绝对核心。虽然目前也支持 Spark、Trino、StarRocks、Doris 的读取,但在多引擎写入和跨云厂商的深度集成上,仍在发展阶段。
- Iceberg:生态的“无冕之王”。其 REST Catalog 协议几乎成为事实标准,获得了 Databricks、Snowflake、Google Cloud、AWS 等所有头部数据平台的全方位原生支持。在多引擎共享和跨组织数据交换上具有不可替代的优势。
4. 架构选型实战建议
根据不同的业务痛点,建议按以下场景对号入座:
场景 A:重度依赖 Flink,要求“分钟级”高频 CDC 入湖与消费
👉 优先选择 Paimon。
它的 LSM 机制、原生的 Changelog 生产以及与 Flink 极度契合的 Checkpoint/Commit 闭环,能让你省去大量调优 Iceberg 小文件和 Compaction 的运维精力。
场景 B:构建跨部门、跨引擎(Spark/Trino/Flink)的统一企业级数据湖
👉 优先选择 Iceberg。
它的规范成熟度最高,引擎兼容性最好。随着 v3 DV 特性的落地,它在应对日常 CDC 同步时的性能已经完全可以满足非极端高频的湖仓需求。
场景 C:拥有海量 JSON/日志/LLM 推理数据,希望“原样入湖,高效直查”
👉 拥抱 Iceberg v3 路线。
v3 的 VARIANT 类型专门为此类场景设计,能完美平衡“Schema-on-read 的灵活性”与“列存的查询性能”。
场景 D:从传统离线数仓(Hive/Greenplum)向现代湖仓平滑迁移
👉 强烈推荐 Iceberg。
它的 Snapshot 机制、Time Travel 以及极其丰富的表迁移工具(如 migrate / snapshot 存储过程),能为离线数仓改造提供最稳妥、最标准化的升级路径。
5. 实战代码与核心配置示例
为了更直观地理解两者在工程落地上的差异,以下整理了针对前文核心论点的典型代码与表属性配置。
5.1 Paimon 典型场景:Flink CDC 高频入湖与流式消费
Paimon 的强项在于极简的流批一体与变更流管理。以下示例展示了如何通过 Flink SQL 将 MySQL 的实时变更无缝同步到 Paimon,并开启内部的 Changelog 供下游流式消费。
1. 创建 Paimon 目标表并配置合并引擎:
-- 在 Flink SQL 环境中创建 Paimon 表
CREATE TABLE paimon_orders (
order_id BIGINT,
user_id BIGINT,
order_amount DECIMAL(10, 2),
status STRING,
update_time TIMESTAMP(3),
PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
'connector' = 'paimon',
'path' = 'hdfs://namenode:8020/user/paimon/warehouse/default.db/paimon_orders',
-- 核心配置 1:开启 Changelog 生产。
-- 'lookup' 模式会在 compaction 时自动生成完整的 -U/+U 变更流,极大地降低了下游消费的成本。
'changelog-producer' = 'lookup',
-- 核心配置 2:对于 MySQL CDC 这种自带完整变更语义的数据源,
-- 也可以使用 'input' 模式直接透传变更,性能最高。
-- 'changelog-producer' = 'input',
-- 核心配置 3:可选开启 Deletion Vectors 以进一步优化点查和合并性能
'deletion-vectors.enabled' = 'true'
);
2. 极简的 CDC 数据写入:
-- 假设 orders_mysql_cdc 是已经建好的 MySQL CDC Source 表
-- 这条普通的 INSERT 语句在 Flink 中会作为持续运行的流任务,将变更实时写入 Paimon
INSERT INTO paimon_orders
SELECT order_id, user_id, order_amount, status, update_time
FROM orders_mysql_cdc;
5.2 Iceberg v3 典型场景:开启 DV 与数仓级 Schema 演进
Iceberg 的强项在于极度灵活的开放规范与多引擎读写。以下示例展示了如何在 Spark 环境中利用 Iceberg v3 的新特性(Deletion Vectors 与 Default Values)来解决更新放大与宽表回填痛点。
1. 创建 Iceberg v3 表并开启 Deletion Vectors:
-- 在 Spark SQL 中创建 Iceberg 表
CREATE TABLE iceberg_catalog.db.iceberg_orders (
order_id BIGINT,
user_id BIGINT,
order_amount DECIMAL(10, 2),
status STRING
)
USING iceberg
TBLPROPERTIES (
-- 核心配置 1:明确指定表格式版本为 v3(或 v2 开启特定属性),以支持高级特性
'format-version' = '3',
-- 核心配置 2:将更新和删除模式设置为 MoR (Merge-on-Read)
'write.update.mode' = 'merge-on-read',
'write.delete.mode' = 'merge-on-read',
-- 核心配置 3:强制底层生成行级删除向量 (Deletion Vectors),而非传统的 Positional Delete 文件
-- 这是彻底解决高频更新下“读放大”痛点的关键
'write.delete.isolation-level' = 'snapshot' -- 结合引擎具体配置项开启 DV
);
2. 体验 v3 的“零成本” Schema 演进(Default Values):
在传统数据湖中,给一张 PB 级大表加列并设置默认值,通常需要重写海量历史数据。而在 Iceberg v3 中,这只是一个极低延迟的元数据操作。
-- 增加一列 'customer_tier',并设置默认值为 'Standard'
-- v3 规范下,这不会重写历史 parquet 文件,读历史数据时引擎会直接返回默认值
ALTER TABLE iceberg_catalog.db.iceberg_orders
ADD COLUMN customer_tier STRING DEFAULT 'Standard';
3. v3 半结构化数据入湖预演(VARIANT 类型):
-- 预留对动态 Schema 和复杂 JSON 嵌套的处理能力(需查询引擎支持)
CREATE TABLE iceberg_catalog.db.events (
event_id STRING,
event_time TIMESTAMP,
-- v3 原生支持 VARIANT,告别 STRING 存 JSON 导致的谓词下推失效问题
payload VARIANT
) USING iceberg TBLPROPERTIES ('format-version' = '3');