Paimon 数据湖 + Gravitino 元数据中心:技术原理与实战指南

Paimon 数据湖 + Gravitino 元数据中心:技术原理与实战指南

一、概述

Apache Paimon 是一个流批一体的数据湖存储格式,支持高吞吐的数据摄入和高效的实时/批量查询。Apache Gravitino 可以作为 Paimon 的统一元数据中心,通过 lakehouse-paimon Catalog 插件直接对接 Paimon 的元数据层,实现:

  • 统一管理 Paimon + 其他数据源(Hive、MySQL、Iceberg、Kafka)的元数据
  • 通过 Gravitino 的 Spark/Flink Connector 透明访问 Paimon 表
  • 对 Paimon 表应用统一的权限控制、标签、策略治理

二、架构总览

复制代码
┌─────────────────────────────────────────────────────────┐
│                     查询引擎层                            │
│   Spark SQL          Flink SQL          Trino           │
│   (Gravitino         (Gravitino         (Gravitino      │
│    Spark Connector)   Flink Connector)   Trino Conn.)   │
└────────┬──────────────────┬──────────────────┬──────────┘
         │                  │                  │
         ▼                  ▼                  ▼
┌─────────────────────────────────────────────────────────┐
│              Gravitino Server (:8090)                    │
│                                                         │
│  ┌─────────────────────────────────────────────────┐    │
│  │           Dispatcher Chain                       │    │
│  │  Event → Normalize → Hook → Operation            │    │
│  └──────────────────────┬──────────────────────────┘    │
│                         │                               │
│  ┌──────────────────────▼──────────────────────────┐    │
│  │         CatalogManager                           │    │
│  │  loadCatalog("my_paimon") → PaimonCatalog        │    │
│  └──────────────────────┬──────────────────────────┘    │
│                         │                               │
│  ┌──────────────────────▼──────────────────────────┐    │
│  │       PaimonCatalogOperations                    │    │
│  │  → PaimonCatalogOps                              │    │
│  │  → Paimon CatalogFactory.createCatalog(...)      │    │
│  └──────────────────────┬──────────────────────────┘    │
└─────────────────────────┼───────────────────────────────┘
                          │
         ┌────────────────┼────────────────────┐
         ▼                ▼                    ▼
   ┌──────────┐    ┌──────────┐         ┌──────────┐
   │Filesystem│    │   JDBC   │         │   Hive   │
   │ (HDFS/   │    │ (MySQL/  │         │Metastore │
   │  S3/OSS) │    │  PG)     │         │(Thrift)  │
   └──────────┘    └──────────┘         └──────────┘
         │                │                    │
         ▼                ▼                    ▼
   ┌─────────────────────────────────────────────┐
   │          Paimon 数据文件 (ORC/Parquet)        │
   │          存储在 HDFS / S3 / OSS / 本地         │
   └─────────────────────────────────────────────┘

三、核心源码解析

3.1 Catalog 插件入口 --- PaimonCatalog

文件 : catalogs/catalog-lakehouse-paimon/src/main/java/.../PaimonCatalog.java

java 复制代码
public class PaimonCatalog extends BaseCatalog<PaimonCatalog> {

  @Override
  public String shortName() {
    return "lakehouse-paimon";  // 创建 Catalog 时 provider 使用此名
  }

  @Override
  protected CatalogOperations newOps(Map<String, String> config) {
    return new PaimonCatalogOperations();  // 核心操作类
  }

  @Override
  protected Capability newCapability() {
    return new PaimonCatalogCapability();  // 能力声明
  }
}

通过 SPI 注册:META-INF/services/org.apache.gravitino.CatalogProvider 中声明了 PaimonCatalog。

3.2 初始化流程 --- PaimonCatalogOperations.initialize()

文件 : catalogs/catalog-lakehouse-paimon/src/main/java/.../PaimonCatalogOperations.java

java 复制代码
@Override
public void initialize(Map<String, String> conf, CatalogInfo info,
    HasPropertyMetadata propertiesMetadata) {
  // 1. 提取 gravitino.bypass.* 前缀的透传属性
  Map<String, String> prefixMap = MapUtils.getPrefixMap(conf, CATALOG_BYPASS_PREFIX);

  // 2. 将 Gravitino 属性转换为 Paimon 属性
  //    catalog-backend → metastore, warehouse → warehouse, uri → uri 等
  Map<String, String> gravitinoConfig =
      ((PaimonCatalogPropertiesMetadata) propertiesMetadata.catalogPropertiesMetadata())
          .transformProperties(conf);

  // 3. 合并配置
  Map<String, String> resultConf = Maps.newHashMap(prefixMap);
  resultConf.putAll(gravitinoConfig);

  // 4. 创建 Paimon 底层 Catalog
  this.paimonCatalogOps = new PaimonCatalogOps(new PaimonConfig(resultConf));
}

在 PaimonCatalogOps 内部,通过 Paimon 官方 API 创建 Catalog:

java 复制代码
// CatalogUtils.loadCatalogBackend()
Catalog catalog = CatalogFactory.createCatalog(CatalogContext.create(options));

3.3 Paimon 支持的 Catalog Backend

java 复制代码
public enum PaimonCatalogBackend {
  FILESYSTEM,   // 本地/HDFS/S3/OSS 文件系统,元数据存在文件中
  JDBC,         // MySQL/PostgreSQL 存储元数据
  HIVE,         // Hive Metastore 存储元数据
  REST          // REST Catalog(如阿里云 DLF)
}
Backend 元数据存储位置 适用场景
filesystem warehouse 目录下的文件 开发测试、简单部署
jdbc MySQL / PostgreSQL 生产环境、多节点共享
hive Hive Metastore 与 Hive 生态共存
rest REST 服务(如阿里云 DLF) 云原生部署

3.4 表操作的内部流程

以 createTable 为例:

复制代码
用户 REST/SDK 请求
  → Gravitino REST API (TableOperations)
    → EventDispatcher → NormalizeDispatcher → HookDispatcher
      → TableOperationDispatcher
        → CatalogManager.loadCatalog("my_paimon")
          → PaimonCatalog (IsolatedClassLoader)
            → PaimonCatalogOperations.createTable()
              → 1. 构建 GravitinoPaimonTable (Gravitino 模型)
              → 2. toPaimonTableSchema() (转为 Paimon Schema)
              → 3. paimonCatalogOps.createTable(identifier, schema)
                → 4. Paimon Catalog.createTable() (真正创建)

关键转换 : GravitinoPaimonTable.toPaimonTableSchema() 将 Gravitino 的列定义、分区、主键、分桶等信息转换为 Paimon 的 Schema 对象。

3.5 属性映射

Gravitino 属性 Paimon 属性 说明
catalog-backend metastore Catalog 后端类型
warehouse warehouse 数仓路径
uri uri 后端连接 URI
jdbc-user jdbc.user JDBC 用户名
jdbc-password jdbc.password JDBC 密码
gravitino.bypass.* * 透传给 Paimon

四、实战 Demo

4.1 前置条件

  1. Gravitino Server 已启动(默认 http://localhost:8090)
  2. 已创建 Metalake(如 my_metalake)并启用
  3. 如使用 HDFS,确保 core-site.xml 和 hdfs-site.xml 放在 catalogs/lakehouse-paimon/conf/ 下

4.2 场景一:Filesystem Backend(开发测试)

创建 Paimon Catalog
bash 复制代码
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" -d '{
  "name": "paimon_dev",
  "type": "RELATIONAL",
  "provider": "lakehouse-paimon",
  "comment": "Paimon dev catalog on local filesystem",
  "properties": {
    "catalog-backend": "filesystem",
    "warehouse": "file:///tmp/paimon-warehouse"
  }
}' http://localhost:8090/api/metalakes/my_metalake/catalogs
创建 Schema
bash 复制代码
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" -d '{
  "name": "ods",
  "comment": "原始数据层"
}' http://localhost:8090/api/metalakes/my_metalake/catalogs/paimon_dev/schemas
创建 Paimon 表(带主键和分区)
bash 复制代码
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" -d '{
  "name": "user_events",
  "comment": "用户行为事件表",
  "columns": [
    {"name": "event_id", "type": "long", "nullable": false, "comment": "事件ID"},
    {"name": "user_id", "type": "long", "nullable": false, "comment": "用户ID"},
    {"name": "event_type", "type": "string", "comment": "事件类型"},
    {"name": "event_time", "type": "timestamp", "comment": "事件时间"},
    {"name": "dt", "type": "string", "nullable": false, "comment": "日期分区"}
  ],
  "partitioning": [
    {"strategy": "identity", "fieldName": ["dt"]}
  ],
  "indexes": [
    {
      "indexType": "primary_key",
      "name": "pk_user_events",
      "fieldNames": [["event_id"], ["dt"]]
    }
  ],
  "properties": {
    "merge-engine": "deduplicate",
    "sequence.field": "event_time"
  }
}' http://localhost:8090/api/metalakes/my_metalake/catalogs/paimon_dev/schemas/ods/tables
查看表详情
bash 复制代码
curl -s http://localhost:8090/api/metalakes/my_metalake/catalogs/paimon_dev/schemas/ods/tables/user_events \
  -H "Accept: application/vnd.gravitino.v1+json" | python3 -m json.tool

4.3 场景二:JDBC Backend(生产环境)

前置:准备 MySQL 元数据库
sql 复制代码
CREATE DATABASE paimon_metastore;

将 MySQL JDBC 驱动(mysql-connector-java-8.x.jar)放到 catalogs/lakehouse-paimon/libs/ 目录下。

创建 Catalog
bash 复制代码
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" -d '{
  "name": "paimon_prod",
  "type": "RELATIONAL",
  "provider": "lakehouse-paimon",
  "comment": "Production Paimon catalog with JDBC backend",
  "properties": {
    "catalog-backend": "jdbc",
    "uri": "jdbc:mysql://mysql-host:3306/paimon_metastore",
    "warehouse": "hdfs://namenode:9000/data/paimon-warehouse",
    "jdbc-user": "paimon",
    "jdbc-password": "your_password",
    "jdbc-driver": "com.mysql.cj.jdbc.Driver"
  }
}' http://localhost:8090/api/metalakes/my_metalake/catalogs

4.4 场景三:Hive Backend(与 Hive 生态共存)

bash 复制代码
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" -d '{
  "name": "paimon_hive",
  "type": "RELATIONAL",
  "provider": "lakehouse-paimon",
  "properties": {
    "catalog-backend": "hive",
    "uri": "thrift://hive-metastore:9083",
    "warehouse": "hdfs://namenode:9000/user/hive/warehouse-paimon"
  }
}' http://localhost:8090/api/metalakes/my_metalake/catalogs

4.5 Java SDK 使用

java 复制代码
import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.rel.*;

// 1. 创建客户端
GravitinoClient client = GravitinoClient.builder("http://localhost:8090")
    .withMetalake("my_metalake")
    .build();

// 2. 创建 Paimon Catalog
Map<String, String> props = ImmutableMap.of(
    "catalog-backend", "filesystem",
    "warehouse", "hdfs://namenode:9000/paimon-warehouse"
);
Catalog catalog = client.createCatalog(
    "paimon_dev", Catalog.Type.RELATIONAL,
    "lakehouse-paimon", "Dev Paimon", props);

// 3. 创建 Schema
catalog.asSchemas().createSchema("ods", "原始数据层", Collections.emptyMap());

// 4. 创建表
Column[] columns = new Column[] {
    Column.of("order_id", Types.LongType.get(), "订单ID", false, false, null),
    Column.of("user_id", Types.LongType.get(), "用户ID"),
    Column.of("amount", Types.DecimalType.of(10, 2), "金额"),
    Column.of("dt", Types.StringType.get(), "日期分区", false, false, null)
};

Transform[] partitions = new Transform[] { Transforms.identity("dt") };

Index[] indexes = new Index[] {
    Indexes.primary("pk_orders", new String[][] {{"order_id"}, {"dt"}})
};

catalog.asTableCatalog().createTable(
    NameIdentifier.of("ods", "orders"),
    columns, "订单表",
    ImmutableMap.of("merge-engine", "deduplicate"),
    partitions, Distributions.NONE, new SortOrder[0], indexes
);

// 5. 查询表
Table table = catalog.asTableCatalog().loadTable(NameIdentifier.of("ods", "orders"));
System.out.println("表名: " + table.name());
System.out.println("列数: " + table.columns().length);

4.6 Python SDK 使用

python 复制代码
from gravitino.client import GravitinoClient
from gravitino.api.catalog import Catalog

client = GravitinoClient(uri="http://localhost:8090", metalake_name="my_metalake")

# 创建 Catalog
catalog = client.create_catalog(
    name="paimon_dev",
    catalog_type=Catalog.Type.RELATIONAL,
    provider="lakehouse-paimon",
    comment="Dev Paimon",
    properties={
        "catalog-backend": "filesystem",
        "warehouse": "file:///tmp/paimon-warehouse"
    }
)

# 创建 Schema
catalog.as_schemas().create_schema("ods", "原始数据层", {})

# 列出表
tables = catalog.as_table_catalog().list_tables(Namespace.of("ods"))

五、通过 Spark 访问 Gravitino 管理的 Paimon

5.1 Spark 配置

bash 复制代码
spark-sql --master local[*] \
  --packages org.apache.gravitino:gravitino-spark-connector-runtime-3.5_2.12:1.2.0 \
  --packages org.apache.paimon:paimon-spark-3.5:1.2.0 \
  --conf spark.plugins=org.apache.gravitino.spark.connector.plugin.GravitinoSparkPlugin \
  --conf spark.sql.gravitino.uri=http://localhost:8090 \
  --conf spark.sql.gravitino.metalake=my_metalake \
  --conf spark.sql.gravitino.enablePaimonSupport=true

5.2 Spark SQL 操作

sql 复制代码
-- 切换到 Gravitino 管理的 Paimon Catalog
USE paimon_dev;

-- 创建数据库
CREATE DATABASE IF NOT EXISTS dwd;
USE dwd;

-- 创建 Paimon 表
CREATE TABLE IF NOT EXISTS user_orders (
  order_id BIGINT,
  user_id BIGINT,
  product_name STRING,
  amount DECIMAL(10,2),
  order_time TIMESTAMP,
  dt STRING
) PARTITIONED BY (dt);

-- 写入数据
INSERT INTO user_orders VALUES
  (1001, 1, '手机', 5999.00, TIMESTAMP '2025-01-15 10:30:00', '2025-01-15'),
  (1002, 2, '笔记本', 8999.00, TIMESTAMP '2025-01-15 11:00:00', '2025-01-15'),
  (1003, 1, '耳机', 299.00, TIMESTAMP '2025-01-16 09:00:00', '2025-01-16');

-- 查询
SELECT user_id, SUM(amount) as total_amount
FROM user_orders
WHERE dt = '2025-01-15'
GROUP BY user_id;

-- Schema Evolution
ALTER TABLE user_orders ADD COLUMNS (coupon_id BIGINT);

-- 查看分区
SHOW PARTITIONS user_orders;

-- 删除分区
ALTER TABLE user_orders DROP PARTITION (dt='2025-01-15');

5.3 Spark 内部原理

复制代码
Spark SQL: USE paimon_dev
  → GravitinoSparkPlugin
    → GravitinoCatalogManager 从 Gravitino 获取 Catalog 列表
    → 发现 paimon_dev (provider=lakehouse-paimon)
    → 注册为 GravitinoPaimonCatalogSpark35
    → PaimonPropertiesConverter 转换属性:
        catalog-backend → metastore
        warehouse → warehouse
    → 内部创建 Paimon SparkCatalog
    → Spark SQL 操作直接走 Paimon SparkCatalog

确保 classpath 包含:

  • gravitino-flink-connector-runtime-1.18_2.12-{version}.jar
  • paimon-flink-1.18-{version}.jar
sql 复制代码
-- 创建 Gravitino Paimon Catalog
CREATE CATALOG paimon_dev WITH (
  'type' = 'gravitino-paimon',
  'gravitino.uri' = 'http://localhost:8090',
  'gravitino.metalake' = 'my_metalake',
  'warehouse' = 'file:///tmp/paimon-warehouse',
  'metastore' = 'filesystem'
);

USE CATALOG paimon_dev;
SHOW DATABASES;

-- 创建表
CREATE TABLE ods.click_events (
  event_id BIGINT,
  user_id BIGINT,
  page_url STRING,
  click_time TIMESTAMP(3),
  PRIMARY KEY (event_id) NOT ENFORCED
);

-- 流式写入
INSERT INTO ods.click_events
SELECT * FROM kafka_source_table;

-- 批量查询
SELECT * FROM ods.click_events;

七、支持的操作和限制

7.1 Schema 操作

操作 支持 备注
创建 Schema 是
删除 Schema 是 支持 cascade
查看 Schema 是
列出 Schema 是
修改 Schema 否 Paimon 不支持

7.2 表操作

操作 支持 备注
创建表 是 支持分区、主键、分桶
查看表 是
列出表 是
修改表 是 支持重命名、加列、改列等
dropTable 否 请使用 purgeTable
purgeTable 是 同时删除元数据和数据

7.3 支持的 Alter Table 操作

  • RenameTable(需单独执行,不能和其他变更混合)
  • AddColumn / DeleteColumn / RenameColumn
  • UpdateColumnComment / UpdateColumnNullability / UpdateColumnPosition / UpdateColumnType
  • UpdateComment(表注释)
  • SetProperty / RemoveProperty

7.4 类型映射

Gravitino 类型 Paimon 类型
Boolean Boolean
Byte / Short / Integer / Long TinyInt / SmallInt / Int / BigInt
Float / Double Float / Double
Decimal(p,s) Decimal(p,s)
String VarChar(Integer.MAX_VALUE)
VarChar(n) VarChar(n)
FixedChar(n) Char(n)
Date Date
Time§ Time§
Timestamp§ LocalZonedTimestamp§
Timestamp_tz§ Timestamp§
Binary / Fixed VarBinary / Binary
Struct / Map / List Row / Map / Array

7.5 限制事项

  1. 分区仅支持 Identity 类型(不支持 bucket/truncate/year/month 等变换分区)
  2. 索引仅支持一个主键(不支持二级索引)
  3. 分桶键必须是主键的子集
  4. 排序(Sort Orders)不支持
  5. Spark 目前仅支持 Filesystem + HDFS 后端
  6. 不支持 MERGE INTO / DELETE / UPDATE / TRUNCATE(Spark/Flink 限制)

八、生产环境建议

8.1 Backend 选择

环境 推荐 Backend 原因
本地开发 filesystem (file://) 零依赖
测试环境 filesystem (HDFS) 简单可靠
生产环境 jdbc (MySQL) 高可用、多节点共享
Hive 混合部署 hive 与 Hive 表共享 Metastore
阿里云 rest (DLF) 全托管

8.2 推荐配置

properties 复制代码
# gravitino.conf 相关
gravitino.catalog.cache.evictionIntervalMs=3600000

# Paimon Catalog 属性(通过 gravitino.bypass.* 透传)
gravitino.bypass.table.type=managed
gravitino.bypass.snapshot.num-retained-max=100
gravitino.bypass.snapshot.time-retained=7d

8.3 HDFS 集成

将以下文件放到 ${GRAVITINO_HOME}/catalogs/lakehouse-paimon/conf/:

  • core-site.xml(HDFS 地址配置)
  • hdfs-site.xml(HDFS HA 配置)
  • Kerberos 相关配置(如有)
相关推荐
物联网软硬件开发-轨物科技6 小时前
【轨物洞见】什么是产品碳足迹(PCF)?ISO 14067 计算方法与应用场景深度解读
大数据·网络·人工智能
GlobalInfo7 小时前
2026年全球无人机清洗系统市场深度分析:规模、竞争格局与AI自主作业演进趋势
大数据·人工智能·ai·无人机
AI职业加油站7 小时前
AI 校招现状:大模型应用工程师证书,助力简历能力证明
大数据·运维·人工智能·学习·职场发展
cc5725026537 小时前
2027 秋招求职分析:大数据相关专业,企业服务售前与实施岗位对比
大数据
专注API从业者7 小时前
告别复杂页面解析,OpenClaw 快速搭建电商商品监控与数据分析脚本
大数据·数据库·python·数据挖掘·数据分析
楚楚2517 小时前
2026企业AI办公工具选型指南:从需求匹配到平台评估
大数据·人工智能
SelectDB8 小时前
Apache Doris x Fluss:面向湖流一体的统一查询与分析
大数据·数据库·数据分析
论文复现现场8 小时前
金融大模型微调需要几张 RTX 4090?QLoRA 配置、工期估算与断点续训
大数据·金融·大模型·分布式训练·模型微调·qlora·rtx4090
阿乔外贸日记8 小时前
全球电子产业高度集中:中、韩、日和中国台湾地区占全球数字硬件产能八成
大数据·服务器·人工智能·物联网·云计算
飞飞传输9 小时前
业务系统文件安全检测怎么做?生物医药企业选型与建设指南
大数据·运维·安全