Paimon存储结构入门
Apache Paimon 的存储结构是一个分层设计的 LSM-Tree(Log-Structured Merge-Tree)架构,专为流批一体场景优化。以下是其完整的存储层级解析:
Catalog ( metastore / 文件系统 )
└── Database ( 命名空间 )
└── Table ( 实体表 )
├── Schema ( schema-0, schema-1... )
├── Snapshot ( snapshot-1, snapshot-2... )
├── Manifest List
├── Manifest File
└── LSM Data Levels ( L0 → L1 → L2... )
二、核心存储目录结构
以文件系统(HDFS/S3/OSS)为存储底座时,表目录组织如下:
${warehouse}/
└── ${database}.db/
└── ${table}/
├── schema/ # 表结构演进历史
│ ├── schema-0
│ ├── schema-1
│ └── ...
├── snapshot/ # 快照管理 (时间旅行核心)
│ ├── snapshot-1
│ ├── snapshot-2
│ └── LATEST # 软链接指向最新快照
├── manifest/ # 清单文件 (数据文件索引)
│ ├── manifest-list-1-0
│ ├── manifest-file-1-0
│ └── ...
├── index/ # 哈希索引 (加速点查)
│ ├── index-1-0
│ └── ...
├── bucket-0/ # 分桶目录 (并发控制单元)
│ ├── data-1-0.orc # 数据文件 (LSM L0层)
│ ├── data-2-0.orc # 数据文件 (LSM L1层)
│ └── ...
├── bucket-1/
└── ...
三、LSM-Tree 内部层级详解
Paimon 采用分层合并策略,数据按主键排序后分布在不同 Level:
层级特性合并策略L0直接写入的 MemTable flush 结果,文件间无序,文件内按主键排序快速追加,不合并L1由 L0 合并而来,文件间有重叠 Key与 L0 触发 Minor CompactionL2+下层文件,Key 范围不重叠,完全有序触发 Major Compaction
文件命名规范:
data-${level}-${id}.${format} (如: data-1-0.orc)
四、关键元数据组件
- Snapshot(快照)
- 作用:实现时间旅行(Time Travel)和读写分离
- 内容:记录当前可见的所有 Manifest List 文件名、提交时间、Watermark
- 格式:JSON
- 生命周期:通过 snapshot.expiration.limit 自动清理JSON
{
"version": 3,
"id": 2,
"schemaId": 0,
"baseManifestList": "manifest-list-2-0",
"deltaManifestList": "manifest-list-2-1",
"changelogManifestList": "manifest-list-2-2", // 仅 changelog-producer=lookup/input 时存在
"commitUser": "flink-job-123",
"commitIdentifier": 9223372036854775807,
"commitKind": "APPEND",
"timeMillis": 1713595200000
}
- Manifest List
- 作用:索引多个 Manifest File,避免单点过大
- 结构:包含 Manifest File 的元数据(文件名、统计信息、分区范围)
- Manifest File
- 作用:记录实际数据文件的物理位置、行数、Min/Max 统计、Null 值数量、Bloom Filter 位置
- 优化:查询时通过统计信息做文件级过滤(Partition Pruning + Predicate Pushdown)
五、数据文件格式(Data File)
Paimon 支持 ORC(默认)和 Parquet 两种列式格式:
内部结构:
Data File (ORC/Parquet)
├── Metadata
│ ├── Schema (含主键、分区键信息)
│ ├── Column Statistics (每列的 min/max/null count)
│ └── Bloom Filter (针对主键和索引列)
├── Row Groups (行组,默认 128MB)
│ └── Columns (列式存储 + 字典编码/Run-Length 编码)
└── Footer
└── Index Data
特殊列:
- KEY:序列化的主键字节(用于去重和合并)
- SEQUENCE_NUMBER:写入序列号(决定同一主键的多版本哪个更新)
- VALUE_KIND:标识是 ADD 还是 DELETE 行(支持 CDC)
六、分桶(Bucket)机制
- 物理划分:数据按 bucket 数散列到不同目录(bucket-0 到 bucket-N-1)
- 并发控制:每个 Bucket 是独立的 LSM-Tree,支持并发写入不同 Bucket
- 动态扩缩容:支持 ALTER TABLE SET (‘bucket’ = ‘new_num’),触发数据重分布
七、索引系统(加速点查)
Paimon 维护独立的哈希索引文件(在 index/ 目录):
Index File 结构
├── Hash Table (主键 → 文件位置)
└── Bloom Filters (减少 IO)
适用场景:
- lookup Changelog Producer:需要快速查找旧值生成 changelog
- 点查查询:SELECT * FROM t WHERE pk = ‘xxx’
八、Changelog 存储(可选)
当 changelog-producer 设置为 input 或 lookup 时:
- 生成独立的 Changelog 数据文件
- 存储在 changelog/ 目录(或复用 manifest 中的 changelogManifestList)
- 格式与数据文件一致,但仅包含变更行
九、写入流程与数据流转
Flink Sink
│
▼
MemTable (内存排序缓冲)
│
▼ (Flush 触发)
L0 File (bucket-x/data-0-y.orc) ──┐
│ │
▼ (Minor Compaction) │
L1 File (bucket-x/data-1-y.orc) ──┤── Manifest 更新
│ │
▼ (Major Compaction) │
L2 File (bucket-x/data-2-y.orc) ──┘
│
▼
Snapshot-2 提交 (原子性重命名)
十、与 Flink State 的协同
Paimon 的部分更新(Partial Update)和聚合(Aggregation)功能依赖 Flink State:
- L0 层:直接写入,不保证全局有序
- Compaction:异步线程合并,生成有序下层文件
- 双写优化:Delta Lake 风格的增量写入 + 后台合并
总结:设计哲学
特性实现方式流批一体LSM 支持实时追加 + 批量合并更新支持主键排序 + Sequence Number 版本控制时间旅行Snapshot 多版本 + 过期清理高效点查Bucket 分区 + Hash Index + Bloom Filter列式分析ORC/Parquet + 统计信息下推
更多推荐




所有评论(0)