每天认识一个组件:Apache Paimon

一、引言

传统数据湖常以 HDFS、S3、OSS 等文件系统或对象存储为底座,通过 Parquet、ORC、Avro 等列式文件承载数据。这类架构成本低、容量大、生态开放,但在实时更新、主键去重、CDC 入湖、增量消费、小文件治理、快照一致性等方面天然不足。

实时数仓过去常见做法是:Kafka 承载实时流,Flink 做计算,结果写入 OLAP 引擎或 KV 存储;离线侧再写入 Hive、Iceberg、Hudi、Delta Lake 等湖仓表。这样虽然能满足部分实时分析,但通常会形成两套链路:一套实时链路,一套离线链路。

Apache Paimon 试图把这两类需求收敛到统一的湖存储表中。它是一个支持流批处理的数据湖格式,可用于基于 Flink 和 Spark 的实时湖仓架构,并通过结合 Lake Format 与 LSM Tree 把实时流式更新带入数据湖。

二、Paimon 是什么

Apache Paimon 是一种湖格式或湖存储表格式。一个 Paimon 表可以描述为由 Schema、快照历史以及快照引用的文件共同组成;读表时,读取器会选择某个快照,再通过 Manifest 找到当前表状态所需的数据文件。

Paimon 的表主要可以分为两类:

表类型 逻辑行为 典型场景 说明
Append Table 不按主键合并,写入行会作为新记录保存 日志、明细事件、批量数据集 没有主键,插入不会进行基于主键的去重或 Upsert
Primary-Key Table 相同主键记录会按 Merge Engine 合并为逻辑行 CDC、Upsert、实体状态更新 维护每个 key 一条逻辑记录,物理上可保留多个版本直到读取或 Compaction 合并

Append Table 更像传统追加型数据湖表,而 Primary-Key Table 是 Paimon 支撑实时更新湖仓的核心。

三、Paimon 的整体架构

Paimon 位于计算引擎与底层存储之间。上层可以由 Flink、Spark、Hive、Trino、StarRocks、Doris 等引擎访问;中间通过 Catalog 管理数据库、表与元数据;底层数据文件与元数据文件存储在 HDFS、对象存储或其他文件系统中。

Paimon 的读写一致性依赖快照,Snapshot 捕获一次已提交的表状态;一个 Snapshot 会引用 Schema、Manifest List,以及可选的 Changelog 和 Index 元数据。

读一个 Paimon 表,大致过程如下:

这种设计的好处是:文件是否存在于存储中,并不直接等于文件属于当前表状态。一个文件只有被已提交快照引用时,才对该快照的用户可见;用户看到的是已提交快照中的文件集合,而不是部分发布的文件。

Paimon 表通常包含以下核心元数据结构:

组件 作用
Schema 定义字段、类型、分区、主键、表选项
Snapshot 一次提交后的表状态
Manifest List 记录一批 Manifest 文件及其统计信息
Manifest 记录数据文件的新增、删除、行数、统计信息等
Data File 存放真实数据,默认格式为 Parquet
Changelog File 在配置 Changelog Producer 时,服务增量读
Index File 存放动态桶索引、删除向量等索引信息

四、核心原理:LSM 与主键表

实时更新数据进入湖仓时,一个绕不开的问题是:数据湖底层文件通常不可原地更新。如果每次更新都重写大文件,写入成本太高;如果只追加新文件,查询时又必须知道哪条记录是最新状态。

Paimon 的 Primary-Key Table 使用 LSM Tree 思路(类似HBase写入模型)解决这个问题。Paimon 将主键数据存储在 LSM Tree 中,Writer 缓冲记录、排序并刷写新文件,Compaction 再合并文件以降低后续读取成本。

也就是说,Paimon 接受"物理上短期存在多个版本",通过读取合并与后台 Compaction 获得逻辑上的最新表状态。

在 Primary-Key Table 中,表或每个分区包含多个 Bucket,每个 Bucket 有自己的 LSM Tree。固定桶模式下,Paimon 会根据bucket-key 的 Hash 选择桶;如果没有设置bucket-key ,通常使用主键列,排除分区列。

rust 复制代码
Primary-Key Table
└── partition dt=2026-09-20
    ├── bucket-0  -> LSM Tree -> data files
    ├── bucket-1  -> LSM Tree -> data files
    ├── bucket-2  -> LSM Tree -> data files
    └── bucket-3  -> LSM Tree -> data files

Bucket 会影响写入分布、读并行度、小文件数量与 Compaction 压力。它不是一个简单的"越多越好"的参数:桶太少会限制并行度,桶太多会放大小文件和元数据开销。

五、Merge Engine:Paimon 更新语义的核心

Paimon 的 Primary-Key Table 通过 Merge Engine 定义相同主键记录如何形成一条逻辑行,目前主要有四类Merge Engine。

Merge Engine 语义 典型输入 适用场景
deduplicate 保留最新记录,默认模式 完整更新行,如 CDC after image 订单状态、用户资料、维表同步
partial-update 用非空字段更新已有行 不同流更新实体的不同字段 用户画像多流拼宽表
aggregation 对同主键字段做聚合 sum、max 等贡献型数据 指标预聚合、实时汇总
first-row 保留第一条,忽略后续同 key 数据 去重事件流 首次访问、首单、首次曝光

一个容易踩坑的点是,"最新"不一定等于"业务时间最新"。如果到达顺序不代表业务顺序,应配置sequence.field ;对于 partial update 的独立流,还可以使用列级 sequence group。

ini 复制代码
业务事件乱序示例
------------------------------------
event_time=10:00  amount=100   晚到
event_time=10:05  amount=120   先到

如果只按到达顺序:
    可能错误地让 10:00 覆盖 10:05

应使用:
    'sequence.field' = 'event_time'

六、Changelog:让下游理解变化

实时链路不只关心"当前表是什么样",还关心"表发生了什么变化"。Paimon 的changelog-producer 控制流式消费者可以获得哪些变化。它与 Merge Engine 不同:Merge Engine 定义表内逻辑行,Changelog Producer 定义流式消费者能收到什么变更。

Producer 旧值来源 适用场景
none 消费者自行维护状态或接受 Upsert 下游是 Upsert Sink,或可自行 Normalize
input 上游输入本身提供完整 changelog 数据库 CDC 已有 before/after
lookup Compaction 时查找旧值生成 changelog 输入没有 before image,但下游需要完整变更
full-compaction 比较两次全量 Compaction 结果 可接受周期性延迟的变更输出

可以这样理解:

sql 复制代码
                Paimon Table State
                       ^
                       |
          +------------+-------------+
          |                          |
    Merge Engine              Changelog Producer
  决定"表里最终是什么"       决定"下游看到什么变化"

对于需要做下游聚合的场景,是否能拿到UPDATE_BEFORE 、UPDATE_AFTER 、DELETE 非常关键。例如金额从 4 变成 5,下游 sum 需要先减 4 再加 5;如果只拿到 5,就必须由消费者自己记住旧值。

七、Paimon 的功能特性与优势

  • 批流一体:Paimon 支持批处理和流处理,这使得一份数据可以同时服务实时链路和离线分析链路,减少 Kafka、OLAP、Hive 多份冗余数据之间的一致性维护成本。
lua 复制代码
Flink Streaming Write
        |
        v
   Paimon Snapshot N
        |
        +------------------> Batch Query: 读取某个快照
        |
        +------------------> Streaming Query: 继续跟踪后续变化
  • 支持主键更新与 CDC 入湖:Primary-Key Table 是 Paimon 的关键能力。它可以维护每个主键一条逻辑行,并让输入记录按 Merge Engine 插入、更新或删除该行。这对 CDC 入湖非常重要。
  • 快照、时间旅行与一致性读取:Paimon 使用 Snapshot 捕获已提交的表状态;较早快照仍可能引用已被后续快照替换的数据文件,因此时间旅行依赖快照和对应文件仍被保留。
rust 复制代码
snapshot-1  -> 昨天 10:00 表状态
snapshot-2  -> 昨天 11:00 表状态
snapshot-3  -> 昨天 12:00 表状态

查询 current:
    使用 snapshot-3

问题回溯:
    指定 snapshot-1 或某个时间点
  • 多 Catalog 与多引擎生态:Paimon Catalog 负责组织数据库、表和元数据,支持filesystem 、hive 、jdbc、rest 等 Metastore 选择:filesystem 适合无外部元服务的简单搭建,Hive Catalog 适合和 Hive 兼容工具共享元数据,JDBC Catalog 适合用关系数据库管理元数据,REST Catalog 适合远程 Catalog 服务与认证场景。
yaml 复制代码
Catalog 选择参考
------------------------------------------------
filesystem : 快速开始、测试、轻量环境
hive       : Hive 生态共享
jdbc       : MySQL/PostgreSQL 等数据库管理元数据
rest       : 服务化 Catalog、统一鉴权、远程访问
  • 查询优化与文件治理:Paimon 支持分区裁剪、文件统计信息、索引、Compaction 等能力。Append Table 文档提到,聚合下推可在部分聚合查询中使用元数据避免读取所有数据行,排序与文件统计可缩小文件值域并改进裁剪,文件索引支持 Bloom filter、bitmap、range bitmap 等过滤能力。这些能力的目标都是减少扫描数据量、降低小文件影响、提升查询规划与执行效率。

八、适用场景

  • CDC 实时入湖:业务库 CDC 入湖、实体表需要 Upsert等。
  • 实时数仓分层:Paimon 可以承载 ODS、DWD、DWS 等实时分层。上游 Flink 写入 ODS,后续 Flink 继续读取 Paimon Changelog 做清洗、关联、聚合,再写回 DWD 或 DWS。适合希望统一实时与离线链路、减少中间存储复制的团队。
  • 用户画像与宽表构建:用户画像常由多个来源更新不同字段:基础资料、行为偏好、会员等级、风控标签、营销标签等。此时partial-update 很有价值,因为它允许用非空字段更新已有行,而不是要求每条记录都携带完整宽表字段。
  • 明细日志与大规模 Append 数据:对于日志、埋点、交易流水、监控指标原始明细等只追加、不按主键更新的数据,Append Table 更合适。
  • 湖仓统一查询层:Paimon 可以被多种引擎访问,支持Flink、Spark、Hive、Trino、StarRocks、Doris 等集成方式,在多引擎共享生产表时,需要先确认所用引擎是否支持目标表特性。

九、部署使用

  • 准备 Connector:下载与计算引擎版本匹配的 Paimon Connector。
  • 创建 Catalog。
ini 复制代码
CREATE CATALOG my_catalog WITH (
    'type' = 'paimon',
    'warehouse' = 'file:/tmp/paimon'
);

USE CATALOG my_catalog;

如果是 HDFS、S3、OSS 等环境,需要把warehouse 替换为相应路径,并配置对应文件系统访问参数。Catalog 也可以选择filesystem 、hive 、jdbc 、rest 等不同 Metastore。

  • 创建主键表。
sql 复制代码
CREATE TABLE orders (
    order_id BIGINT,
    customer_id BIGINT,
    amount DECIMAL(12, 2),
    status STRING,
    update_time TIMESTAMP,
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
    'bucket' = '4',
    'merge-engine' = 'deduplicate',
    'sequence.field' = 'update_time'
);
  • 写入数据。
sql 复制代码
INSERT INTO orders VALUES
(1, 1001, 99.90, 'CREATED', TIMESTAMP '2026-09-20 10:00:00');

INSERT INTO orders VALUES
(1, 1001, 109.90, 'PAID', TIMESTAMP '2026-09-20 10:05:00');
  • 查询数据。
sql 复制代码
//查询结果应保留 order_id = 1 的最新逻辑状态
SELECT * FROM orders WHERE order_id = 1 ;

+----------+-------------+--------+--------+---------------------+
| order_id | customer_id | amount | status | update_time          |
+----------+-------------+--------+--------+---------------------+
| 1        | 1001        | 109.90 | PAID   | 2026-09-20 10:05:00 |
+----------+-------------+--------+--------+---------------------+
  • 创建 Append Table。
sql 复制代码
CREATE TABLE user_events (
    user_id BIGINT,
    event_type STRING,
    event_time TIMESTAMP,
    dt STRING
) PARTITIONED BY (dt) WITH (
    'bucket' = '-1'
);

十、与其他湖格式的关系

Paimon 与 Iceberg、Hudi、Delta Lake 都属于湖仓表格式方向,但它的特色在于更强调流式更新、主键表、LSM、Changelog 与 Flink 实时处理的结合,Paimon 通过结合 Lake Format 与 LSM Tree,把实时流式更新带入数据湖架构。

一个不严谨但便于理解的类比是:

markdown 复制代码
Iceberg / Delta / Hudi:
    更常被理解为通用湖仓表格式,强调快照、事务、演进、查询优化等能力

Paimon:
    同样具备湖仓表能力,但更突出流式写入、主键更新、LSM、Changelog 和实时湖仓

十一、常见问题与建议

1.为什么查询结果不是刚写入就可见?

在 Flink 流式写入中,数据通常需要随 checkpoint 提交为 Paimon Snapshot 后才对读者可见。

2.deduplicate 与partial-update 怎么选?

如果每条更新都是完整行,使用deduplicate 更简单。如果每条更新只包含部分列,且空值表示"不更新该字段",则应考虑partial-update 。

3.是否一定要配置 Changelog Producer?

不一定。默认changelog-producer = none 不写单独完整 changelog,适合下游接受 Upsert 或自行维护状态的场景。但如果下游需要完整 before/after 语义,例如做撤回式聚合,则需要评估input 、lookup 或full-compaction。

4.多 Writer 写同一张表安全吗?

Paimon 使用乐观并发控制。Writer 会独立准备文件,再基于最新已提交 Snapshot 验证并原子发布;如果其他 Writer 先提交,则需要基于新状态重试,若文件冲突则提交会被拒绝。建议:尽量让不同 Writer 写不同分区,对 Compaction 使用独立作业,降低多个 Writer 同时重写相同文件的冲突。

5.Bucket 数怎么设置?

Bucket 数会影响写入并行度、读取并行度、小文件数量和 Compaction 负担。置得过大,导致小文件和元数据压力上升;设置太小,则可能限制吞吐和并行读取。

markdown 复制代码
数据量小 / 入门测试:
    使用较小固定 bucket,例如 1、2、4

稳定大表 / 高吞吐写入:
    结合主键分布、写入并行度、文件大小、Compaction 压力压测

分区差异大 / 数据增长不可预测:
    评估动态 bucket 或 postpone bucket,但需确认版本与引擎支持

6.查询慢通常从哪里排查?

现象 可能原因 排查建议
查询规划慢 Manifest 过多、小文件多 检查 Compaction、Manifest 合并
扫描数据多 分区设计不合理、统计信息不足 优化分区、排序、文件统计、索引
主键表读慢 LSM 层级多、Compaction 不及时 调整 Compaction 参数
流式消费延迟高 Changelog 生成方式成本高 评估 lookup、full-compaction 延迟与资源
多 Writer 失败 Snapshot 或文件冲突 检查写入分区、锁、独立 Compaction
相关推荐
程序员梅雨1 小时前
ZooKeeper面经总结
java·大数据·linux·分布式·后端·zookeeper·中间件
星云低代码开发平台1 小时前
付款单、发票和银行回单联查:字段口径与异常分支
大数据
深圳市益普科技有限公司2 小时前
中小封装厂上 MES,为什么十个项目九个坑?
大数据·数据库·人工智能
2601_962966642 小时前
2027数据科学与大数据技术专业秋招:B端产品与技术售前的能力准备
大数据
Elastic 中国社区官方博客3 小时前
Elastic 线下 Meetup 将于 2026 年 12 月 19 号下午在西安举行
大数据·数据库·elasticsearch·搜索引擎·ai·数据分析·全文检索
Data analyse4563 小时前
Session超时与浏览器休眠的计时校验
大数据·前端·数据库·数据分析
zandy10113 小时前
世界模型产业主体梳理:具身大脑、物理仿真与数字场景的多元实践
大数据·人工智能
字节跳动数据平台3 小时前
爆点靠人找、素材靠人剪?交给 LAS 算子,从选点到成片自动完成
大数据
码流子4 小时前
高速场景视频接入:GB/T 28181 级联与 RTSP 取流,一条链路讲清楚
大数据·数据库·人工智能·物联网·音视频