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.xmlhdfs-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 相关配置(如有)
相关推荐
Promise微笑2 小时前
2026年国产替代油介损测试仪:油介损全场景解决方案与技术演进
大数据·网络·人工智能
workflower3 小时前
具身智能行业应用-生活服务业
大数据·人工智能·机器人·动态规划·生活
志栋智能4 小时前
超自动化安全:构建智能安全运营的核心引擎
大数据·运维·服务器·数据库·安全·自动化·产品运营
xiaoduo AI4 小时前
客服机器人非工作时间能休眠?智能Agent开放平台定时唤醒,无人值守省资源?
大数据·人工智能·机器人
好赞科技5 小时前
深度测评2026年精选美发预约小程序排行榜 革新预约新体验 修订
大数据·微信小程序
集和诚JHCTECH7 小时前
BRAV-7120加持,让有毒有害气体无处遁形
大数据·人工智能·嵌入式硬件
互联网志8 小时前
加速高校科技成果转化 赋能实体经济高质量发展
大数据·人工智能·物联网
李可以量化8 小时前
DeepSeek 量化交易实战:用标准化提示词模板实现 AI 辅助交易决策
大数据·数据库·人工智能
学掌门9 小时前
数据分析师职业规划——数据分析师的职业焦虑与未来发展
大数据·信息可视化
亚马逊云开发者9 小时前
EMR Core 节点部署 Flink Client 实战:Bootstrap Action 一次打包多次复用,解决调度系统提交任务的痛点
大数据·flink·bootstrap