跳转至

Iceberg REST Catalog

Explanation · 原理资深

先分清 · 本页讲协议,不讲实现

Iceberg REST Catalog 有两层极易被揉在一起:

  1. 协议层(本页主体)· Apache Iceberg 维护的 spec · 定义 HTTP API · 不是可运行的产品
  2. 实现层(本页附表引用)· Polaris / Nessie / UC / Gravitino / Glue 等各自的服务端实现 · 能力、成熟度、兼容度参差

读者最容易的误读:把某实现的能力当成协议已稳定提供的能力("Polaris 支持 X 所以 REST 协议支持 X")。本页的兼容矩阵段(§机制 6)专门拆这种混淆。

一句话定位

Iceberg 官方定义的 HTTP/REST 层 Catalog 协议。把"表在哪里、哪个 snapshot 是当前的、怎么 commit"标准化为一套 API——任何实现遵守协议即可互通。在新建 Iceberg 项目里正在成为默认协议(但不等于整个 Lakehouse 世界的"事实标准"——Glue / Unity 商业版 / HMS 在各自语境下仍是主流)。

TL;DR

  • 协议层事实:HTTP/REST + JSON · 引擎端用同一个 RESTCatalog 客户端
  • 解决引擎 × Catalog 矩阵爆炸(以前每个引擎 × 每个 Catalog 各自实现)
  • CAS 提交规范化——所有实现都做同一套并发冲突检测
  • 协议层支持:namespaces / tables / views / snapshot commit / config
  • 权限 / 多租户协议留给实现方(OAuth + Bearer token 是常规选择)
  • 实现方(各自产品,各自成熟度):Apache Polaris · Nessie · Gravitino · Snowflake Open Catalog · Databricks UC · AWS Glue(走自家 API 而非 REST)
  • 语境声明:新建 Iceberg 项目里是默认;Databricks / AWS 栈里常共存其他 Catalog;Hadoop 时代栈很多仍跑 HMS

1. 它解决什么 · Catalog 混战时代

早期 Iceberg 每家 Catalog 自己一套实现: - HiveCatalog(接 HMS) - GlueCatalog(接 AWS Glue) - JDBCCatalog(自己用 MySQL/PG) - NessieCatalog(接 Nessie) - HadoopCatalog(file-based)

引擎端的挑战: - Spark Iceberg 扩展要适配 N 种 Catalog 实现 → 代码膨胀 - Trino Iceberg Connector 同样要一一适配 - 跨云(A 云用 Glue、B 云用自建)→ 两份代码 - 想自建 Catalog 服务 → 没有标准 API 可遵循

REST Catalog 的价值:一份协议,任意实现,引擎端只认协议。

对比 HMS 的优势

维度 Hive Metastore Iceberg REST Catalog
协议 Thrift(老、贵) HTTP/REST + JSON
多租户 内建(OAuth)
扩展性 锁定 Hive schema 协议可演进
云原生 自部署 thrift server 容器化 friendly
Commit 语义 表锁 CAS 返回 409 冲突重试
生态 逐渐淘汰 所有主流引擎都支持

工业现状(2024-2026)

  • Snowflake 2024-08 开源 Apache Polaris 作为参考实现(2026-Q2 仍孵化 · 1.3.0-incubating)
  • Databricks 2024-06 把 Unity Catalog 核心 OSS(LF AI & Data 沙箱)· 商业版兼容 Iceberg REST
  • Tabular(Ryan Blue 团队)· 2024-06 被 Databricks 收购 · 原 Tabular 商业服务并入 Databricks;Iceberg 生态权力结构转折事件——Ryan Blue 等核心作者转向 Databricks,Iceberg REST spec 演进话语权重心位移
  • Nessie 既有自有 API 也暴露 Iceberg REST(Git-like 能力是差异化)
  • Apache Polaris / Unity / Nessie / Gravitino 的 Iceberg REST 兼容度参差——见下"兼容度矩阵"段

REST Catalog 已成Iceberg 生态的新默认Apache 维持协议治理的中立性(spec 变更走社区投票),但商业实现方(Databricks 通过收购的 Tabular 团队 / Snowflake 通过 Polaris)的实际需求正在驱动 spec 演进方向——OAuth2 / Vended Credentials / Scan Planning / Multi-table Commit 的优先级都受商业场景推动。

Tabular 2024-06 被 Databricks 收购:原 Tabular REST 实现团队并入 Databricks,Iceberg 社区里原独立的 Tabular 技术方向与 Databricks 产品路线汇合——对读者的含义是:Iceberg REST spec 的实际演进重心向 Databricks 倾斜,但 Apache 层面协议治理仍独立。

2. 协议深挖

端点总览

类别 示例 作用
Config GET /v1/config 客户端初始化、协议能力协商
Namespace GET/POST/DELETE /v1/namespaces 数据库级
Table GET/POST/DELETE /v1/{ns}/tables/{t} 表 CRUD
Rename POST /v1/tables/rename 改名
Commit POST /v1/{ns}/tables/{t} 原子提交新 metadata
Update POST /v1/{ns}/tables/{t}/metrics 统计上报
View GET/POST/DELETE /v1/{ns}/views/{v} View 管理
Transaction POST /v1/transactions/commit 多表事务

提交流程(CAS 语义)

sequenceDiagram
  participant C as Client (Spark/Trino)
  participant R as REST Catalog
  participant O as Object Store
  C->>R: GET /v1/ns/tables/t  (读当前 metadata)
  R-->>C: metadata v42 + base-location
  C->>O: 写新 data files + metadata v43
  C->>R: POST /v1/ns/tables/t<br/>body: {updates, requirements: [AssertCurrentMeta=v42]}
  alt requirements 满足(没人并发修改)
    R-->>C: 200 OK + new metadata v43
  else 别人已提交 v43
    R-->>C: 409 Conflict (CommitFailedException)
    C->>C: retry or rebase
  end

关键requirements 字段是 CAS 的核心——提交时告诉服务端"我基于 v42 算的这次改动,请确保当前还是 v42"。

请求 / 响应示例

创建表

POST /v1/namespaces/db/tables
Content-Type: application/json

{
  "name": "orders",
  "schema": {...},
  "partition-spec": [{"name": "days_ts", "transform": "day", "source-id": 5}],
  "properties": {"format-version": "2"}
}

Commit(写入):

POST /v1/namespaces/db/tables/orders
{
  "requirements": [
    {"type": "assert-ref-snapshot-id", "ref": "main", "snapshot-id": 1234}
  ],
  "updates": [
    {"action": "add-snapshot", "snapshot": {...}},
    {"action": "set-snapshot-ref", "ref-name": "main", "type": "branch", "snapshot-id": 1235}
  ]
}

协议支持的高级能力

  • Branches / Tags(Iceberg v2+)
  • View(视图)(Iceberg View Spec 1.0 · 2023 ratified)
  • Multi-table Transaction(一个原子提交跨多张表 · spec 2024-2025 演进中)
  • Vended Credentials(服务端签发临时 S3 token,客户端不持长期 credential)
  • Scan Planning(spec 1.5+ · 服务端完成剪枝返回 split 计划;详见下"机制 4")
  • OAuth2(认证层 · 和 Vended Credentials 的授权分工见"机制 5")

3. 关键机制

机制 1 · Vended Credentials

安全机制:客户端不直接持有S3 长期 credential。

Client --auth--> REST Catalog
REST Catalog --sts--> AWS STS (权限范围限定在此表)
REST Catalog --> Client: 临时 token (TTL = 15 min)
Client --temp token--> S3

好处: - 最小权限(每张表独立 token) - 审计每次数据访问 - 多租户隔离

机制 2 · 多租户隔离

Warehouse A  → Namespace A1, A2 ...
Warehouse B  → Namespace B1, B2 ...

每个 Warehouse 独立 S3 bucket prefix + 独立 IAM Role。

机制 3 · 协议扩展能力

协议预留扩展点: - Custom metadata properties - 自定义 Request Body 字段(backward-compatible) - Signal capabilities in /v1/config

例:新能力"向量索引"可以作为一类表资产,Catalog 实现支持就行,协议端不改。

机制 4 · Scan Planning · 从"客户端规划"到"服务端规划"

传统 Iceberg 读:客户端下载 metadata.json → manifest list → 所有相关 manifest,自己做分区剪枝和 split 生成。数量级:小表几百 KB · 百万分区大表单次读元数据可达几十 MB · 对 mobile / edge / 高并发 Trino worker 都是延迟与带宽负担。

Scan Planning endpoint(REST Catalog spec 1.5+):

POST /v1/{prefix}/namespaces/{ns}/tables/{table}/plan
  body: { snapshot-id, filter, case-sensitive, select-fields }
  response: { plan-status, plan-tasks: [...], file-scan-tasks: [...] }

服务端完成: - Manifest 读取和剪枝 - Predicate pushdown 到 file-level stats - 生成 split 计划(或返回 inline data 用于小表)

收益: - Client 不必下载元数据 → mobile / edge 场景可行 - 服务端可用 Bloom / 额外 index 做更准剪枝(客户端没有这些) - 多租户下元数据本就不该全暴露 → 安全收益 - 为 SaaS 形态湖仓(Snowflake / Databricks Managed / S3 Tables)铺路

风险: - 服务端成本上移 · 需要规划能力 scale-out - Client 和 Server 升级不同步时功能降级路径要设计好

机制 5 · OAuth2 + Vended Credentials · 认证 / 授权分工

两个机制互补但解决不同问题

机制 作用 颗粒
OAuth2 认证("你是谁") 客户端身份 · 每个 API 调用
Vended Credentials 授权到存储("你能访问哪些 S3 object") 每表 / 每次读 · STS 临时 token

端到端流程

1. Client → IdP (OIDC) → access_token (JWT)
2. Client → REST Catalog: Authorization: Bearer <access_token>
3. REST Catalog 校验 token + 决定授权范围(OAuth2 scope)
4. REST Catalog → AWS STS: AssumeRole + Session policy(限定到该表所属 S3 prefix;跨多表读时各自独立签发)
5. REST Catalog → Client: { data-files: [...], credentials: { access_key, secret, session_token, expiry } }
6. Client 用临时 token → S3 直读

关键性质

  • Client 从不持长期 credential · 只有短期 OAuth token 和分钟级 STS token
  • Catalog 能看到每次"谁读了哪张表" · 审计链完整
  • Scope 缩小:STS 只给本表 prefix 的 s3:GetObject · 越权被阻断

Trino / Spark 客户端 对这套流程已有内置支持(iceberg.rest-auth.type = oauth2 等配置)。Polaris 的 Credential Vending 2026-01 加了 SigV4 / KMS per-catalog / 位置限制等增强。

机制 6 · 各实现的 Iceberg REST 兼容矩阵

时效声明 · 快照时点 2026-Q2

本矩阵是 2026-Q2 快照——这类信息季度级变化。Polaris / UC / Nessie / Gravitino 各自 2026 都有 roadmap 推进;建议: - 选型时以各实现最新一次 release note 为准 - 本手册会尽量季度更新,但不要把表格当长期稳定事实 - 能力边界看概念类别(是否支持 View / Multi-table / Vended)而不是"√/×"瞬时截图

能力 实现 Apache Polaris Unity Catalog OSS Nessie Gravitino
Tables CRUD
Namespaces
Views 部分 部分
Branches / Tags 部分 ✅(+ Nessie 跨表分支) 依底层
Multi-table Transaction 部分 ✅(原生 Git-like)
Vended Credentials ✅(SigV4 · 1.3) 部分 部分 依底层
Scan Planning 2026 roadmap
OAuth2 Auth

表格说明: - "部分"= 该实现有对应能力但覆盖不完整(如只支持子集语义 / 只对特定资源类型生效) - "roadmap"= 计划中 / 预览 · 非 GA · 选型时不应当成可用能力 - "SigV4" = AWS Signature V4 签名协议;Polaris 1.3+ 支持,更老版本的 Vended Credentials 覆盖面较窄

解读(概念层,不挂具体版本): - Polaris · Iceberg 原生特性覆盖最全 · Snowflake 主力投入 - Nessie · 分支 / 多表事务是它的 differentiator · 其他能力和 Polaris 平级 - UC OSS · 核心 CRUD 够用 · 高级特性(View / Multi-table)靠 Databricks 商业版 - Gravitino · 联邦层 · 自己不是 REST 实现 · 能力上限 = 被联邦的底层 Catalog 能力 - AWS Glue · 不是 Iceberg REST 实现 · 走自家 API(GlueCatalog),能力谱系独立(详见 glue.md

4. 工程细节

典型部署

Client (Spark/Trino/Flink)
        │ HTTPS
Load Balancer
REST Catalog Service (Polaris / 自研)
        ├── Metadata DB (Postgres / MySQL / DynamoDB)
        ├── IAM / OAuth
        └── S3 (数据)

客户端配置

Spark

spark.conf.set("spark.sql.catalog.my_cat", "org.apache.iceberg.spark.SparkCatalog")
spark.conf.set("spark.sql.catalog.my_cat.type", "rest")
spark.conf.set("spark.sql.catalog.my_cat.uri", "https://catalog.corp/v1")
spark.conf.set("spark.sql.catalog.my_cat.warehouse", "s3://my-lake/warehouse")
spark.conf.set("spark.sql.catalog.my_cat.credential", "client:secret")

Trino

connector.name=iceberg
iceberg.catalog.type=rest
iceberg.rest-catalog.uri=https://catalog.corp/v1
iceberg.rest-catalog.security=OAUTH2
iceberg.rest-catalog.oauth2.credential=client:secret

PyIceberg

from pyiceberg.catalog.rest import RestCatalog
catalog = RestCatalog(
    "prod",
    uri="https://catalog.corp/v1",
    warehouse="s3://my-lake/warehouse",
    credential="client:secret",
)

生产配置建议

  • HA:REST Catalog 服务至少 3 副本 + Load Balancer
  • Metadata DB:Postgres 主从 + 定时备份
  • 缓存:客户端 metadata cache TTL 30s-5min(Iceberg 原生支持)
  • 限流:按 client ID 限 commit QPS(避免雪崩)
  • 审计:所有 commit / read 写 audit log

5. 性能数字

操作 典型延迟
Load Table(读当前 metadata) 5-30ms
Commit(CAS 成功) 10-100ms
Commit(冲突重试) +几十 ms 每次
Namespace list < 10ms
QPS(单 REST 节点) 1k-5k
Metadata DB 规模 10k+ tables 健康

6. 代码示例

pyiceberg 端到端

from pyiceberg.catalog.rest import RestCatalog
from pyiceberg.schema import Schema
from pyiceberg.types import NestedField, LongType, StringType, TimestampType
import pyarrow as pa

catalog = RestCatalog(
    "prod",
    uri="https://catalog.corp/v1",
    warehouse="s3://my-lake/warehouse",
)

# 建表
schema = Schema(
    NestedField(1, "id", LongType(), required=True),
    NestedField(2, "name", StringType()),
    NestedField(3, "ts", TimestampType()),
)
catalog.create_table("db.users", schema=schema)

# 读表
table = catalog.load_table("db.users")
df = table.scan(row_filter="id > 100").to_pandas()

# 写表
data = pa.Table.from_pydict({"id": [1, 2], "name": ["a", "b"], "ts": [...]})
table.append(data)

Spark 用法

-- 使用 REST Catalog 的表
CREATE TABLE my_cat.db.orders (
  order_id BIGINT,
  amount   DECIMAL(18,2),
  ts       TIMESTAMP
) USING iceberg
PARTITIONED BY (days(ts));

-- Time Travel
SELECT * FROM my_cat.db.orders VERSION AS OF 123456;

Trino 用法

SHOW CATALOGS;
USE my_cat.db;
SHOW TABLES;
SELECT * FROM orders WHERE ts >= DATE '2024-12-01';

7. 陷阱与反模式

  • OAuth 配置错误:客户端和服务端端点 / scope 不一致 → 403
  • 忽略 Vended Credentials:直接给客户端长期 S3 Key → 权限泄露风险
  • Commit 重试死循环:冲突率高时 naive retry → CPU 飙;应加 指数退避 + rebase
  • Metadata DB 单点:REST Service 多副本但 DB 没 HA → 单点故障
  • 协议版本错配:客户端用新协议、服务端旧 → /v1/config 协商必做
  • 没做审计:合规查不回"谁改了哪张表"
  • 跨 Warehouse 跨账号:S3 Bucket Policy 没配对 → Access Denied
  • 长 Commit 未超时:客户端挂起 → 资源泄露;服务端 timeout 30s

8. 横向对比 · 延伸阅读

权威阅读

相关