第3章:Catalog体系深度解析
总览:什么是Catalog?
Catalog 是Paimon的元数据管理中心,负责统一管理所有数据库、表及其Schema信息。
可以把Catalog想象成一个"图书馆管理系统":
- Catalog = 图书馆本身
- Database = 图书馆的各个楼层/区域
- Table = 图书的具体书名
- Schema = 书的目录结构(章节、页数等)
Catalog核心设计
3.1 Catalog接口设计
核心功能分类
Catalog提供以下核心功能:
java
Catalog接口 {
// 数据库操作
createDatabase(name, properties)
dropDatabase(name, cascade)
listDatabases()
getDatabase(name)
alterDatabase(name, changes)
// 表操作
createTable(identifier, schema)
dropTable(identifier)
getTable(identifier)
listTables(databaseName)
alterTable(identifier, schemaChanges)
// Schema管理
getSchema(identifier) -> TableSchema
// 版本管理(如果支持)
createTag(identifier, tag, snapshot)
deleteTag(identifier, tag)
rollbackTo(identifier, instant)
// 分布式锁
lockFactory() -> Optional<LockFactory>
// 文件系统
fileIO() -> FileIO
}
多种实现
Paimon提供了多种Catalog实现:
| 实现 | 元数据存储 | 特点 | 适用场景 |
|---|---|---|---|
| FileSystemCatalog | 文件系统(HDFS/S3等) | 简单、独立、无外部依赖 | 数据湖、BI系统 |
| HiveCatalog | Hive Metastore | 与Hive生态兼容 | Hive用户、数据仓库 |
| JdbcCatalog | 数据库(MySQL/PG) | 高可用、支持事务 | 生产环境、对可用性要求高 |
| RESTCatalog | 远程REST服务 | 集中管理、多集群共享 | 多租户、云原生 |
| CachingCatalog | 其他Catalog + 本地缓存 | 性能优化 | 频繁访问元数据 |
3.2 FileSystemCatalog实现原理
什么(What):FileSystemCatalog如何存储元数据?
FileSystemCatalog将所有元数据存储在文件系统上:
bash
warehouse/
└── db_name/ # 数据库目录
├── .paimon/ # Paimon系统目录
│ └── database # 数据库配置文件
└── table_name/
├── .paimon/
│ ├── schema/
│ │ └── schema-0.json # Schema版本
│ └── options # 表选项
└── data/ # 数据目录
为什么(Why):为什么选择文件系统?
| 优点 | 具体体现 |
|---|---|
| 无外部依赖 | 无需部署Hive、Postgres等 |
| 高可用 | 元数据就是数据,与数据一起备份 |
| 云友好 | 支持HDFS、S3、OSS等所有云存储 |
| 成本低 | 利用现有存储基础设施 |
| 易于调试 | 可以直接查看JSON文件 |
怎样(How):实现细节
创建数据库:
java
catalog.createDatabase("mydb", ignoreIfExists=true);
执行步骤:
1. 检查warehouse/mydb/.paimon/database是否存在
2. 如果不存在,写入文件:
warehouse/mydb/.paimon/database (包含数据库名称)
3. 检查完成
创建表:
java
Schema schema = new Schema(
fields(...),
primaryKeys("id"),
options({"bucket": "4"})
);
catalog.createTable(
Identifier.create("mydb", "users"),
schema
);
执行步骤:
1. 创建表目录:warehouse/mydb/users/
2. 写入Schema文件:warehouse/mydb/users/.paimon/schema/schema-0.json
3. 写入选项文件:warehouse/mydb/users/.paimon/options
4. 初始化数据目录
读取表元数据:
java
Table table = catalog.getTable(Identifier.create("mydb", "users"));
执行步骤:
1. 读取schema-0.json → 获得表结构
2. 读取options文件 → 获得表配置
3. 创建FileStoreTable实例 → 用户可见
3.3 元数据管理与Schema演化
什么是Schema演化(Schema Evolution)?
Schema演化是指在不丢失现有数据的前提下,修改表结构的能力。
例如:
sql
-- 初始表
CREATE TABLE users (id INT, name VARCHAR(50));
-- 添加新列
ALTER TABLE users ADD COLUMN age INT;
-- 删除列
ALTER TABLE users DROP COLUMN name;
-- 重命名列
ALTER TABLE users RENAME COLUMN age TO user_age;
Paimon如何支持Schema演化?
核心原理:版本化Schema
bash
时刻1:schema-0.json
{
"fields": [
{"id": 0, "name": "id", "type": "INT"},
{"id": 1, "name": "name", "type": "VARCHAR(50)"}
]
}
↓ 添加age列
时刻2:schema-1.json
{
"fields": [
{"id": 0, "name": "id", "type": "INT"},
{"id": 1, "name": "name", "type": "VARCHAR(50)"},
{"id": 2, "name": "age", "type": "INT"}
]
}
↓ 删除name列
时刻3:schema-2.json
{
"fields": [
{"id": 0, "name": "id", "type": "INT"},
{"id": 2, "name": "age", "type": "INT"}
]
}
特点:
- 每次Schema修改都生成新版本
- 列的ID(id字段)永不改变
- 旧数据继续使用旧Schema,新数据使用新Schema
- 读取时自动适配不同版本的数据
读取过程:
kotlin
查询:"SELECT id, age FROM users WHERE id=1"
执行:
1. 扫描Manifest,找到data-file-1(使用schema-0)和data-file-2(使用schema-2)
2. 读data-file-1时:schema-0没有age列 → 填充NULL
3. 读data-file-2时:schema-2有age列 → 正常读取
4. 合并结果:
(id=1, age=NULL) from data-file-1
(id=1, age=25) from data-file-2
常见Schema演化操作
操作1:添加列
java
List<SchemaChange> changes = new ArrayList<>();
changes.add(SchemaChange.addColumn("age", DataTypes.INT()));
catalog.alterTable(
Identifier.create("db", "users"),
changes
);
效果:
- 新数据包含age列
- 旧数据的age值为NULL
操作2:删除列
java
List<SchemaChange> changes = new ArrayList<>();
changes.add(SchemaChange.dropColumn("age"));
catalog.alterTable(
Identifier.create("db", "users"),
changes
);
效果:
- 新数据不含age列
- 旧数据忽略age列
操作3:修改列类型
java
List<SchemaChange> changes = new ArrayList<>();
changes.add(SchemaChange.updateColumnType(
"age",
DataTypes.BIGINT() // INT → BIGINT
));
注意:
- 需要确保类型兼容(可自动转换)
- 不支持不兼容的转换(如STRING → INT)
3.4 CachingCatalog缓存优化
为什么需要缓存?
在高频查询场景下,频繁读取文件系统元数据会成为瓶颈:
sql
查询1:SELECT * FROM users WHERE id=1
→ 读取Schema
→ 读取FileStore配置
→ 执行查询
查询2:SELECT * FROM users WHERE id=2
→ 再次读取Schema (重复!)
→ 再次读取FileStore配置 (重复!)
→ 执行查询
CachingCatalog的作用:缓存频繁访问的元数据
scss
┌──────────────────────────┐
│ CachingCatalog │
│ ┌────────────────────┐ │
│ │ 本地缓存(内存) │ │
│ │ schema, filestore │ │
│ └────────┬───────────┘ │
│ ↓ │
│ ┌────────────────────┐ │
│ │ 下层Catalog实现 │ │
│ │(FileSystem/Hive) │ │
│ └────────────────────┘ │
└──────────────────────────┘
缓存策略
时间驱动缓存:
java
Options options = new Options();
options.set(CacheOption.ENABLE, true);
options.set(CacheOption.EXPIRE_AFTER_WRITE, Duration.ofMinutes(10));
Catalog catalog = CatalogFactory.createCatalog(options);
效果:
- 缓存在内存中最多保留10分钟
- 10分钟后自动过期,重新从底层读取
大小驱动缓存:
java
options.set(CacheOption.CACHE_SIZE, "100MB");
效果:
- 当缓存大小超过100MB时,淘汰最近未使用的条目
- 使用LRU算法
手动失效:
java
// 更新Table后,需要主动清除缓存
catalog.alterTable(identifier, schemaChanges);
((CachingCatalog)catalog).invalidateCache(); // 清除所有缓存
或针对性清除:
((CachingCatalog)catalog).invalidateTable(identifier); // 清除特定表的缓存
总结:Catalog的核心价值
1. 统一的元数据视图
scss
Flink用户
↓
Catalog(统一元数据入口)
↓
┌─────────────┬─────────────┬──────────────┐
Spark用户 Hive用户 Python用户
2. 灵活的后端实现
scss
应用代码只依赖Catalog接口
↓
具体实现可以灵活替换
FileSystemCatalog → HiveCatalog
(开发环境) (生产环境)
3. Schema演化支持
sql
业务需求变化
↓
表结构调整(ALTER TABLE)
↓
现有数据 + 新数据 无缝兼容
4. 性能优化
CachingCatalog + 本地缓存
↓
元数据访问延迟 降低 10-100倍
↓
查询响应时间显著提升