流式数据湖Paimon探秘之旅 (一) Paimon整体架构概览

第1章:Paimon整体架构概览

总览:什么是Paimon?

Apache Paimon是一个创新的湖格式(Lake Format),它赋予Flink和Spark构建**实时湖仓架构(Realtime Lakehouse)**的能力。Paimon的前身是Flink Table Store,融合了LSM(Log-Structured Merge-tree)结构,带来了流式实时更新能力,打破了传统数据仓库批量处理的限制。

简单来说,Paimon = 湖格式 + LSM树 = 流批一体处理。


核心架构设计

架构设计图

graph TB subgraph "计算引擎层" Flink["Flink"] Spark["Spark"] Hive["Hive"] end subgraph "API层" Catalog["Catalog
元数据管理"] Table["Table
表抽象"] end subgraph "核心存储层" FileStore["FileStore
存储引擎"] subgraph "主键表" KVStore["KeyValueFileStore"] LSM["LSM Tree"] MergeEngine["Merge Engine"] Compaction["Compaction"] end subgraph "追加表" AppendStore["AppendOnlyFileStore"] Clustering["Clustering"] end end subgraph "操作层" Scan["FileStoreScan
扫描"] Write["FileStoreWrite
写入"] Commit["FileStoreCommit
提交"] Read["SplitRead
读取"] end subgraph "元数据层" Snapshot["Snapshot
快照"] Manifest["Manifest
清单"] ManifestList["ManifestList
清单列表"] Schema["Schema
表结构"] end subgraph "文件系统层" HDFS["HDFS"] S3["S3"] OSS["OSS"] LocalFS["Local FS"] end Flink --> Catalog Spark --> Catalog Hive --> Catalog Catalog --> Table Table --> FileStore FileStore --> KVStore FileStore --> AppendStore KVStore --> LSM LSM --> MergeEngine LSM --> Compaction Table --> Scan Table --> Write Table --> Commit Table --> Read Scan --> Manifest Write --> Manifest Commit --> Snapshot Commit --> Manifest Snapshot --> ManifestList ManifestList --> Manifest Manifest --> HDFS Manifest --> S3 Manifest --> OSS Manifest --> LocalFS

写入流程时序图

sequenceDiagram participant User as 用户应用 participant Table as FileStoreTable participant Write as FileStoreWrite participant Buffer as WriteBuffer participant Writer as RecordWriter participant Commit as FileStoreCommit participant Snapshot as SnapshotManager User->>Table: newWrite() Table->>Write: 创建Write实例 User->>Write: write(record) Write->>Buffer: 写入内存缓冲 alt 缓冲区满 Buffer->>Writer: flush到磁盘 Writer->>Writer: 生成数据文件 end User->>Write: prepareCommit() Write->>Write: 刷新所有缓冲 Write-->>User: 返回Committable User->>Commit: commit(committables) Commit->>Commit: 冲突检测 Commit->>Manifest: 写入Manifest文件 Commit->>ManifestList: 更新ManifestList Commit->>Snapshot: 创建新Snapshot Snapshot->>Snapshot: 原子提交 Commit-->>User: 提交成功

读取流程时序图

sequenceDiagram participant User as 用户应用 participant Table as FileStoreTable participant Scan as FileStoreScan participant Snapshot as SnapshotManager participant Manifest as ManifestFile participant SplitRead as SplitRead participant Reader as RecordReader User->>Table: newScan() Table->>Scan: 创建Scan实例 User->>Scan: plan() Scan->>Snapshot: 获取Snapshot Snapshot-->>Scan: 返回快照信息 Scan->>Manifest: 读取Manifest Manifest-->>Scan: 返回文件列表 Scan->>Scan: 过滤与分区裁剪 Scan-->>User: 返回Split列表 User->>Table: newRead() Table->>SplitRead: 创建Read实例 loop 每个Split User->>SplitRead: createReader(split) SplitRead->>Reader: 创建RecordReader loop 读取记录 User->>Reader: readBatch() Reader->>Reader: 文件读取与解码 Reader-->>User: 返回数据批次 end end

Compaction压缩流程时序图

sequenceDiagram participant Manager as CompactManager participant Strategy as UniversalCompaction participant Task as CompactTask participant Rewriter as CompactRewriter participant MergeEngine as MergeEngine participant Writer as DataFileWriter Manager->>Manager: triggerCompaction() Manager->>Strategy: pick(levels) Strategy->>Strategy: 选择待压缩文件 Strategy-->>Manager: 返回CompactUnit Manager->>Task: 创建CompactTask Task->>Task: 文件分区 Task->>Rewriter: rewrite(sections) loop 每个Section Rewriter->>Rewriter: 创建MergingReader loop 合并记录 Rewriter->>MergeEngine: merge(key, values) MergeEngine->>MergeEngine: 应用合并逻辑 MergeEngine-->>Rewriter: 返回合并结果 Rewriter->>Writer: write(record) end Writer->>Writer: closeForCommit() Writer-->>Rewriter: 返回新文件 end Rewriter-->>Task: 返回CompactResult Task-->>Manager: 返回待提交文件 Manager->>Manager: commitCompact() Manager->>Manager: 更新Manifest

Snapshot提交流程时序图

sequenceDiagram participant Commit as FileStoreCommit participant Lock as CatalogLock participant Conflict as ConflictChecker participant Manifest as ManifestFile participant ManifestList as ManifestList participant Snapshot as Snapshot Commit->>Lock: lock() Lock-->>Commit: 获取锁成功 Commit->>Conflict: 检查冲突 Conflict->>Snapshot: 读取最新Snapshot Conflict->>Conflict: 比较文件变更 alt 存在冲突 Conflict-->>Commit: 抛出异常 Commit->>Lock: unlock() else 无冲突 Conflict-->>Commit: 验证通过 Commit->>Manifest: 写入新Manifest Manifest-->>Commit: 返回文件路径 Commit->>ManifestList: 更新清单列表 ManifestList-->>Commit: 返回列表路径 Commit->>Snapshot: 创建新Snapshot Note over Snapshot: commitIdentifier++
manifestList
baseManifestList Snapshot->>Snapshot: 原子写入JSON Snapshot-->>Commit: Snapshot创建成功 Commit->>Lock: unlock() end

核心设计理念与多维解析

1.1 什么是Paimon - Lake Format的设计理念

什么(What):Paimon是什么?

Paimon是一个开源的通用文件格式,用于在云存储(HDFS、S3、OSS等)上构建数据湖。它定义了数据在文件系统上的组织方式,类似于Iceberg和Hudi,但有自己的独特设计。

关键特性对比表:

特性 Paimon Iceberg Hudi
流式更新 ✓ (LSM优化) △ (有但不是重点)
批处理
Schema演化
多引擎支持 Flink/Spark 多引擎 Spark/Hive
设计导向 流式实时优先 批处理优先 写入优化
为什么(Why):为什么需要Paimon?

传统的数据处理面临一个难题:流和批的割裂

场景举例: 你运营一个电商平台,需要:

  • 实时处理:用户点击、购买等事件需要立即更新到用户画像表
  • 批量处理:每天生成销售报告、计算用户留存率等

传统方案:

  • 使用消息队列(Kafka) + 流处理引擎(Flink) → 实时更新
  • 使用数据仓库(Hive) + 批处理引擎(Spark) → 离线分析
  • 问题:两套系统、两套数据、难以同步

Paimon方案:

  • 统一使用Paimon作为存储格式
  • Flink可以实时写入和更新数据
  • Spark可以读取最新快照进行分析
  • 优势:一套系统、一份数据、天然一致性
怎样(How):Paimon如何解决问题?

Paimon通过以下关键设计实现流批统一:

  1. LSM树结构:专为流式更新优化

    • 写入优化:小批量数据快速写入
    • 读取优化:自动压缩合并最新数据
  2. 快照机制(Snapshot):保证一致性视图

    • 每次提交生成一个快照
    • 可以基于快照进行批量读取
    • 可以从特定快照恢复
  3. 主键表与追加表:两种表满足不同场景

    • 主键表:支持更新删除(Update/Delete)
    • 追加表:仅支持追加(Append-Only)

1.2 模块结构与依赖关系

Paimon项目的分层架构:
scss 复制代码
┌─────────────────────────────────────────────────────────┐
│           计算引擎集成层                                  │
│   paimon-flink  │  paimon-spark  │  paimon-hive       │
└────────┬────────┴────────┬────────┴─────────┬─────────┘
         │                 │                   │
┌────────┴─────────────────┴───────────────────┴─────────┐
│              API层(paimon-api)                         │
│  提供Table、Catalog等高级接口                           │
└────────┬────────────────────────────────────────────┘
         │
┌────────┴─────────────────────────────────────────────┐
│          核心存储层(paimon-core)                       │
│  FileStore、主键表、追加表、LSM实现                     │
└────────┬────────────────────────────────────────────┘
         │
┌────────┴────────┬──────────────┬──────────────┐──────┐
│                 │              │              │      │
│  格式层      │ 公共库      │ 文件系统      │代码生成│
│ paimon-     │paimon-    │paimon-      │paimon-│
│ format      │common    │filesystems  │codegen│
│             │          │             │       │
│ (Parquet/   │(数据结构、│(HDFS/S3/   │(性能 │
│  ORC/Lance) │工具类)  │ OSS/本地)   │优化) │
└─────────────┴──────────┴──────────────┴──────┘
核心模块详解:
模块 功能 核心类
paimon-core 存储引擎核心 FileStore, FileStoreTable, Snapshot
paimon-common 公共工具和数据结构 InternalRow, RowType, BinaryRow
paimon-format 文件格式支持 ParquetFormat, OrcFormat
paimon-filesystems 多文件系统适配 FileIO接口的多种实现
paimon-flink Flink集成 FlinkCatalog, Source, Sink
paimon-spark Spark集成 SparkCatalog, DataSource
paimon-codegen 代码生成 性能优化的序列化/反序列化

1.3 核心概念:FileStore、Table、Catalog

三层抽象设计:
graph TB A["Catalog
元数据管理"] -->|contains| B["Database
数据库"] B -->|contains| C["Table
表"] C -->|uses| D["FileStore
存储引擎"] D -->|manages| E["Files & Metadata
文件与元数据"] style A fill:#e1f5ff style B fill:#fff3e0 style C fill:#f3e5f5 style D fill:#e8f5e9 style E fill:#fce4ec
1.3.1 Catalog(目录/元数据管理)

定义:Paimon的Catalog负责管理所有元数据(数据库、表、Schema等)。

关键接口方法

scss 复制代码
Catalog {
  // 数据库操作
  createDatabase(name)
  dropDatabase(name)
  listDatabases()
  getDatabase(name)
  
  // 表操作
  createTable(identifier, schema)
  dropTable(identifier)
  getTable(identifier)
  listTables(database)
  
  // Schema操作
  alterTable(identifier, schemaChanges)
  getSchema(identifier)
}

实现类

  • FileSystemCatalog:基于文件系统存储元数据(最常用)
  • HiveCatalog:使用Hive Metastore存储元数据
  • CachingCatalog:在其他Catalog基础上添加缓存

场景示例

less 复制代码
// 创建Catalog
Catalog catalog = CatalogFactory.createCatalog(
    "filesystem", 
    options // {warehouse: /data/warehouse}
);

// 获取表
Table table = catalog.getTable(Identifier.create("db", "users"));

// 修改Schema
catalog.alterTable(
    Identifier.create("db", "users"),
    SchemaChange.addColumn("new_col", DataTypes.STRING())
);
1.3.2 Table(表抽象)

定义:Table是用户与Paimon交互的主接口,提供读写能力。

核心方法

java 复制代码
Table {
  // 读取操作
  TableScan newScan()
  TableRead newRead()
  
  // 写入操作
  TableWrite newWrite(user)
  
  // 提交操作
  TableCommit newCommit(user)
  
  // 元数据
  TableSchema schema()
  List<String> primaryKeys()
  List<String> partitionKeys()
}

两种表类型

类型 特点 支持操作 使用场景
主键表 有主键定义 Insert/Update/Delete 用户表、订单表、维度表
追加表 无主键定义 Insert Only 日志表、事件表、归档表

场景示例

java 复制代码
// 主键表 - 用户表
Table usersTable = catalog.getTable(
    Identifier.create("db", "users")  // pk: user_id
);

// 可以进行更新
TableWrite write = usersTable.newWrite("app");
write.write(row);  // 如果user_id已存在,则更新
write.prepareCommit();

// 追加表 - 日志表
Table logsTable = catalog.getTable(
    Identifier.create("db", "logs")  // no primary key
);

// 仅支持追加
logsTable.newWrite("app").write(row);  // 必然是新增
1.3.3 FileStore(存储引擎)

定义:FileStore是实际管理数据文件的存储引擎,负责读写、压缩、管理。

核心接口

java 复制代码
FileStore<T> {
  // 扫描操作
  FileStoreScan newScan()
  
  // 读写操作
  SplitRead<T> newRead()
  FileStoreWrite<T> newWrite(user)
  FileStoreCommit newCommit(user)
  
  // 管理操作
  SnapshotManager snapshotManager()
  ChangelogManager changelogManager()
  
  // 配置
  CoreOptions options()
  BucketMode bucketMode()
}

实现类

  • KeyValueFileStore:主键表的存储实现(使用LSM树)
  • AppendOnlyFileStore:追加表的存储实现

工作流程

复制代码
写入流程:
用户数据 → FileStoreWrite → 内存缓冲 → 文件输出 → Manifest元数据 → Snapshot快照

读取流程:
Snapshot → Manifest → 文件列表 → FileStoreScan → Split生成 → SplitRead → 用户数据

场景示例

java 复制代码
FileStoreTable table = (FileStoreTable) 
    catalog.getTable(Identifier.create("db", "orders"));

// 获取存储引擎
FileStore store = table.store();

// 创建扫描器
FileStoreScan scan = store.newScan();
List<Split> splits = scan.plan().splits;

// 创建读取器
SplitRead read = store.newRead();
for (Split split : splits) {
    RecordReader reader = read.createReader(split);
    // 读取数据
}

// 创建写入器
FileStoreWrite write = store.newWrite("app");
write.write(record);

1.4 两种表类型:主键表 vs 追加表

对比与选择
graph LR A["创建表时
指定Primary Key"] -->|有主键| B["主键表
KeyValueFileStore"] A -->|无主键| C["追加表
AppendOnlyFileStore"] B -->|支持| B1["Insert/Update/Delete
完整DML"] B -->|使用| B2["LSM Tree
流式优化"] B -->|适用| B3["实时业务数据
高并发更新"] C -->|支持| C1["Insert Only
仅追加"] C -->|使用| C2["简单追加结构
批处理优化"] C -->|适用| C3["日志/事件数据
顺序写入"]
主键表详解

定义:具有唯一主键约束的表,支持完整的DML操作(Insert/Update/Delete)。

核心特性

  1. 自动去重:同一主键的多次写入只保留最新版本
  2. 支持更新:可以修改已存在的记录
  3. 支持删除:可以删除记录
  4. LSM优化:使用LSM树加速流式写入

Schema定义

java 复制代码
// 创建主键表
Schema schema = new Schema(
    fields(
        field(0, "user_id", DataTypes.INT()),      // ← 主键字段1
        field(1, "name", DataTypes.VARCHAR(50)),
        field(2, "age", DataTypes.INT()),
        field(3, "balance", DataTypes.DECIMAL(10, 2))
    ),
    partitionKeys("region"),  // 可选:分区键
    primaryKeys("user_id")    // ← 定义主键
);

catalog.createTable(
    Identifier.create("db", "users"),
    schema
);

工作原理:

当你写入同一用户的多条记录时:

ini 复制代码
时刻1: 写入 (user_id=1, name='Alice', age=25, balance=1000)
时刻2: 写入 (user_id=1, name='Alice', age=26, balance=1500)  ← 年龄更新
时刻3: 写入 (user_id=1, name='Alice', age=27, balance=2000)  ← 年龄和余额更新

最终查询结果: (user_id=1, name='Alice', age=27, balance=2000)
← 自动保留最新版本

实时场景应用

java 复制代码
// Flink流处理:实时更新用户余额
StreamExecutionEnvironment env = 
    StreamExecutionEnvironment.getExecutionEnvironment();

DataStream<RowData> source = env
    .fromSource(kafkaSource, ...)  // 从Kafka读取用户事件
    .name("Kafka Source");

// 每条事件都可能更新用户表的某个字段
source.sinkTo(new PaimonSink(table));

env.execute();

// 同时,可以用Spark进行实时查询
Spark.sql("SELECT * FROM db.users WHERE age > 25")
追加表详解

定义:无主键约束的表,仅支持追加操作(Append-Only)。

核心特性

  1. 追加仅:只能新增记录,不能更新或删除
  2. 简单高效:不需要去重,直接写入
  3. 批处理优化:专为顺序扫描优化

Schema定义

java 复制代码
// 创建追加表 - 注意没有primaryKeys参数
Schema schema = new Schema(
    fields(
        field(0, "event_id", DataTypes.LONG()),
        field(1, "user_id", DataTypes.INT()),
        field(2, "event_type", DataTypes.VARCHAR(50)),
        field(3, "timestamp", DataTypes.TIMESTAMP(3)),
        field(4, "payload", DataTypes.STRING())
    ),
    partitionKeys("dt")  // 按日期分区
    // primaryKeys() 不指定!
);

catalog.createTable(
    Identifier.create("db", "events"),
    schema
);

工作原理

css 复制代码
日志表写入流程(无去重):
事件1: {event_id: 1, user_id: 1, event_type: 'click', ...}
事件2: {event_id: 2, user_id: 1, event_type: 'click', ...}  ← 重复的事件也会保存
事件3: {event_id: 3, user_id: 2, event_type: 'purchase', ...}

查询结果: 包含所有3条记录(不去重)

日志场景应用

java 复制代码
// 处理日志流
DataStream<Event> eventStream = env
    .fromSource(kafkaSource, ...)
    .name("Event Stream");

// 直接写入追加表,无须担心重复
eventStream
    .map(event -> convertToRowData(event))
    .sinkTo(new PaimonSink(logTable));

// 批量分析:统计用户行为
Spark.sql("""
    SELECT 
        user_id,
        event_type,
        COUNT(*) as count
    FROM db.events
    WHERE dt >= '2024-01-01'
    GROUP BY user_id, event_type
""")
选择指南
需求 主键表 追加表
需要更新记录
需要删除记录
去重要求
高频实时更新
仅追加日志
历史全量保留
存储空间 需去重/压缩 节省(直接追加)
查询性能 需扫描多版本 高(顺序扫描)

总结:Paimon架构的核心价值

关键设计创新:

  1. 流批一体化

    • LSM树为流式写入优化
    • Snapshot为批量读取提供一致视图
    • 同一份数据既能流处理又能批处理
  2. 灵活的表类型

    • 主键表处理业务数据的变化
    • 追加表保存历史日志的完整性
    • 一个框架满足两种场景
  3. 多层次抽象

    • Catalog管理元数据生命周期
    • Table提供友好的操作接口
    • FileStore是物理存储实现
  4. 多引擎支持

    • Flink:实时流处理
    • Spark:批量离线分析
    • Hive/Presto:OLAP查询

实际应用场景:

场景1:电商实时数据仓库

scss 复制代码
用户行为日志(Kafka)
    ↓
Flink实时处理 → 更新用户表(主键表)
    ↓
Paimon(统一存储)
    ↓
Spark日间分析 + 实时查询

场景2:日志去重与分析

markdown 复制代码
原始日志流(多个来源)
    ↓
Flink去重 → Paimon追加表
    ↓
Spark/Hive统计分析

场景3:流式机器学习特征

markdown 复制代码
用户实时特征更新 → Paimon主键表
    ↓
模型训练系统 → 定期生成snapshot
    ↓
在线推理 + 离线评估

思考题

  1. 为什么Paimon选择LSM树而不是其他结构?

    • 提示:考虑流式小批量写入的特点
  2. 主键表和追加表可以在同一个Catalog中共存吗?

    • 答案:可以,不同表可以有不同配置
  3. Paimon比Hive的优势在哪里?

    • 提示:考虑实时性、更新能力、成本等方面

相关推荐
梦里不知身是客111 小时前
帆软的图标类型介绍
python·信息可视化·数据分析
咚咚王者2 小时前
人工智能之数据分析 Matplotlib:第六章 知识总结
人工智能·数据分析·matplotlib
a***59262 小时前
爬虫基础之爬取某基金网站+数据分析
爬虫·数据挖掘·数据分析
笨蛋少年派11 小时前
跨境电商大数据分析系统案例:③建模、分析与暂时收尾
hive·数据挖掘·数据分析
Cisyam^11 小时前
openGauss + LangChain Agent实战:从自然语言到SQL的智能数据分析助手
sql·数据分析·langchain
CC数学建模12 小时前
被问爆的 “高颜值 + 强功能” 学生管理系统!Flask+MySQL 全栈开发,自带数据分析 + 幸福指标,毕设 / 竞赛直接
mysql·数据分析·flask
咚咚王者15 小时前
人工智能之数据分析 Matplotlib:第四章 图形类型
人工智能·数据分析·matplotlib
语落心生16 小时前
大宗供应链企业舆情指标系统设计(一)舆情指标设计
数据分析
语落心生17 小时前
餐饮供应链的数仓设计思考 (五) 系统稳定性与SLA保障体系
数据分析