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 相关配置(如有)
相关推荐
失败才是人生常态2 小时前
大数据基础学习
大数据·学习
北京软秦科技有限公司2 小时前
IACheck AI报告文档审核:高端制造标准引用报告审核的智能导航
大数据·人工智能·制造
中烟创新2 小时前
技术深耕,全域赋能:中烟创新产品矩阵与OpenClaw实现深度融合
大数据·人工智能
QYR_Jodie2 小时前
稳健增速托举刚需扩容:全球X射线荧光光谱仪(XRF)2026-2032年CAGR4.8%,2032年剑指11.73亿美元
大数据·人工智能·市场报告
昨夜见军贴06162 小时前
IACheck AI报告文档审核助力新能源汽车:降低电磁兼容(EMC)检测报告风险,确保合规性与准确性
大数据·人工智能·汽车
zml.~2 小时前
Spark大数据分析:解锁海量数据价值的核心利器
大数据·数据分析·spark
岁岁种桃花儿2 小时前
Flink从入门到上天系列第十四篇:Flink当中的处理函数
大数据·flink
我爱学习好爱好爱2 小时前
Elasticsearch 7.17.10 双节点集群部署实战(基于 Rocky Linux 9.6)
大数据·linux·elasticsearch
AC赳赳老秦3 小时前
2026 AI原生工具链升级:DeepSeek与AI原生IDE深度联动,重塑开发效率新高度
大数据·ide·人工智能·web3·去中心化·ai-native·deepseek