[数据湖] Apache Iceberg : 一种面向海量分析型数据集的开放表格式

1 概述

image

https://iceberg.org.cn

产品介绍

  • 产品定位:Apache Iceberg 是一种面向海量分析型数据集的开放表格式(Open Table Format),并非【数据库】、【查询引擎】或【存储系统】。

它在【对象存储】(S3 / GCS / ADLS / HDFS)上的 Parquet/ORC/Avro 数据文件之上,叠加一层元数据与事务抽象,让原始文件拥有接近 SQL 表的语义(ACID、Schema 演化、隐藏分区、时间旅行)。

  • 诞生背景与原因
  • 2017 年前后 Netflix 在生产中大量使用 Hive 表,遇到三大痛点:
  • 无可靠 ACID 事务Schema 变更需重写数据依赖目录 listing 做分区裁剪导致大规模下表规划极慢
  • Ryan Blue 与 Daniel Weeks 主导启动 Iceberg,用“文件级元数据树 + 原子提交”替代“目录即分区”的 Hive 模型。
  • 解决的核心问题

    • 仓库级可靠性(原子提交、Serializable 隔离、乐观并发)下沉到【数据湖】;
    • 【查询引擎】无需 list 目录即可做文件级/列级裁剪;
    • 支持Add/Drop/Rename/Reorder 列而【无需重写数据】
    • 通过 Snapshot 机制原生支持【时间旅行与回滚】
    • 成为【引擎中立】标准,Spark / Trino / Flink / Hive / Impala / Snowflake / BigQuery / DuckDB 均可读同一份表。
  • Apache Iceberg URL

发展历程

  • 2017:Netflix 内部启动,初版 Java 实现。
  • 2018-11:开源并捐赠给 Apache 软件基金会,进入 Incubator。
  • 2020-05:毕业为 Apache Top-Level Project(TLP)
  • ~2022(V2 Spec):引入 Delete Files(position / equality delete),支持行级 UPDATE/DELETE/MERGE,脱离“【只能 append/overwrite 分区】”。
  • 2025-09:1.10.0 稳定线;随后 V3 Spec 落地(Deletion Vectors、VARIANT 类型、Row Lineage、geometry/geography、timestamp_ns、默认列值等)。
  • 2026-05:发布 1.11.0,完整实现 V3,Deletion Vectors 生产可用,增加服务端 Scan Planning、表级加密、Spark 4.x / Flink 2.x 支持。
  • 2026 至今V4 Spec 在邮件列表与设计文档阶段推进(single-file commit、Content Stats、relative path、列式元数据等),尚未发布可量产 spec。

核心功能

  • ACID 与并发控制:每次写入产生新 Snapshot,Catalog 指针原子切换;多写者走乐观并发 + 序列号冲突重试,读者永不看到部分写入。
  • 完整 Schema Evolution:基于字段 ID 追踪,支持【加列/删列/改名/调序/安全类型拓宽】,历史数据文件无需重写,不会出现“【僵尸数据】”。
  • Hidden Partitioning(隐藏分区):用户按原始列写 WHERE ts > ...,Iceberg 自动应用 month/day/bucket/truncate/hour 等 transform 做【分区裁剪】;分区列不污染业务 Schema
  • Partition Evolution:分区策略可随负载演变(如从 day(ts)hour(ts)),新旧布局共存,无需重写全表。
  • Time Travel & Rollback:按 Snapshot ID 或时间戳读取历史一致视图;可一键回滚到良好 Snapshot。
  • 行级变更:V2 起支持 position/equality delete;V3 起推荐 Deletion Vector(Puffin + Roaring Bitmap),MERGE/UPDATE/DELETE 免大规模重写。
  • 数据压缩与维护:自带 rewrite_data_files(bin-pack / sort / Z-order / Hilbert 实验)、snapshot expire、orphan file clean。
  • 多引擎同表:同一份 Iceberg 表可被 Spark 写、Trino 读、Flink 流灌、Snowflake 外表挂载。

主要特点

  • 真正开放:Apache 2.0 协议,ASF 治理,无单一厂商绑定;spec 与实现分离(Java 参考实现 + Python/Go/Rust 实现)。
  • 引擎与 Catalog 中立:Catalog 可为 Hive MetaStore / AWS Glue / JDBC / Nessie / Polaris(REST) / Hadoop 本地;引擎侧各厂商自行实现 SPI。
  • 元数据树避免目录 listing:Catalog → Metadata JSON → Manifest List → Manifest → Data File,规划复杂度为【元数据跳转】而非 O(n) 目录遍历。
  • 列统计内嵌:Manifest 内记录 per-file min/max/null_count/bytes,支持细粒度文件裁剪。
  • 存储格式解耦:数据文件可用 Parquet(主流)/ ORC / Avro

局限性

  • 自身不是【查询引擎】:性能取决于 Spark/Trino 等【消费方】;小文件、过期 Snapshot、orphan file 需【主动运维】。
  • 流式高频写入有 commit 放大:V3 下每次 commit 仍可能写 metadata.json + manifest list + manifest;V4 正试图用 single-file commit 缓解。
  • Equality Delete 读放大历史包袱:V2 表若未 compaction,delete 文件过多会拖慢【读操作】;需依赖后台 compaction 策略。
Equality Delete(等值删除)是 Apache Iceberg Spec V2​ 引入的两种 Delete File 之一(另一种是 Position Delete),用来在不重写数据文件、且不知道行物理位置的前提下,实现行级 DELETE / UPDATE / MERGE。
核心思想:用“列值”而不是“文件+行号”来标记哪些行逻辑上已删除。

它解决的问题与产生背景
  1 对象存储不可原地改行,V1 只能 Copy-on-Write 整文件重写;
  2 CDC / Flink 流上游只给你 user_id=12345 已删除 这种业务键值,并不清楚这行在 `s3://.../part-007.parquet` 文件的第几行;
  3 `Equality Delete` 让 `writer` 无需扫描【数据文件】来定位行位置,直接把 `{user_id:12345}` 写进一个小 delete 文件即可,写路径极轻。
  • Catalog 选型影响事务语义:Hadoop catalog 仅靠文件 rename 提供弱原子;生产建议 REST/JDBC/Nessie/Glue 等强一致型的 catalog
  • 跨引擎能力不对齐:V3 特性(Deletion Vector / Variant)在 Spark 支持好,Trino/DuckDB 等滞后,【混用引擎】需注意 feature matrix

image

Apache Iceberg Compatibility Matrix/Iceberg 兼容性矩阵

适用场景

  • 基于 S3/OSS/COS 构建开放 Lakehouse,拒绝数仓锁定、被厂商锁定。
  • 多引擎共治:Spark 批处理 + Flink 流注入 + Trino/Dremio 交互式 BI + Python(PyIceberg) 直接分析。
  • 需要时间旅行、审计、回滚、CDC 入湖的金融/电商/日志平台。
  • 宽表 Schema 频繁演进(埋点、特征表、LLM embedding 表)。
  • 【多云/混合云】下共享同一逻辑表给不同团队。

同类竞品

  • Delta Lake:Databricks 系出身,Spark 深度集成,UniForm 桥接 Iceberg;Iceberg 在“多引擎中立 + Catalog 多样”上更彻底。
  • Apache Hudi:Uber 开源,擅长流式 CDC / MOR 表 / 索引,对近实时 upsert 场景成熟;Iceberg 在开放生态与 BI 引擎覆盖面更广。
  • Paimon(原 Flink Table Store):面向 Flink 流批一体,主键表 LSM 结构;与 Iceberg 定位部分重叠但内核不同。
  • 商业 Warehouse 外表:Snowflake Iceberg Table / BigQuery Iceberg / Databricks Iceberg v3 —— 它们消费 Iceberg,而不是替代 Iceberg。

发展趋势

  • 社区活跃度:Dev 邮件列表、GitHub PR、Rust/Python/REST Catalog/Terraform Provider 多语言生态同步迭代;2026 年焦点在 V4 元数据降本(single-file commit、Content Stats、relative path)。

  • Star / Fork 趋势:GitHub 长期位居数据湖格式榜首,2026 年周新增 Star 维持高位(外部观测约 9k+ 区间波动),Fork 与 contributor 数持续增长,Netflix/Apple/Airbnb/Snowflake/Databricks 均有人参与 spec 讨论。

  • 总结Iceberg 已成为开放 Lakehouse 表格式的事实标准,未来 12–18 个月的主线是从“功能完备的 V3”走向“元数据更轻、提交更便宜的 V4”,并进一步【统一多引擎元数据互操作】

2 工作原理与架构

概念术语

  • Table Format:定义数据文件如何被组织成表、如何提交变更、如何做事务的规范,不等于存储格式。
  • Catalog:表的“入口指针服务”,记录 tableName → 当前 metadata.json 路径;原子换指针即完成 commit。
  • Metadata File(metadata/*.json):存 schema、partition specs、sort orders、snapshot 列表、当前 snapshot-id、properties。
  • Snapshot:表在某次提交后的不可变一致性视图;每次写产生新 snapshot。
  • Manifest List(*.avro):一个 snapshot 对应一个,列出本 snapshot 涉及的 manifest 文件及分区级摘要。
  • Manifest File(*.avro):记录一组 data file 的路径、partition tuple、row count、列级 min/max/null_count、sequence number。
  • Data File / Delete File:实际 Parquet/ORC/Avro 数据;V2 起 delete file 标记被删行;V3 起推荐 Deletion Vector 存于 Puffin。
  • Hidden Partition Transformidentity/month/day/hour/bucket/truncate/hour 等函数,查询用原始列、物理按 transform 分区。
  • Sequence Number:manifest/snapshot 单调增序号,解决多写者顺序与 delete 应用顺序。

架构与运行原理 *

  • 三层逻辑架构(Catalog / Metadata / Data):
flowchart TD Client["Spark / Trino / Flink / PyIceberg"] -->|1. 解析表名| Catalog["Catalog<br/>(HMS/Glue/Nessie/Polaris-REST/JDBC)"] Catalog -->|2. 返回 current-metadata.json 路径| Client Client -->|3. 读 metadata.json| Meta["Metadata Layer<br/>schema / partition-spec / snapshots[]"] Meta -->|4. current-snapshot-id → manifest-list| ML["Manifest List (Avro)"] ML --> M1["Manifest File A"] ML --> M2["Manifest File B"] M1 --> D1["Data File parquet-1"] M1 --> D2["Data File parquet-2 + DV(puffin)"] M2 --> D3["Data File parquet-3"] Client -->|5. 按统计裁剪 manifest/data| Read["Scan 计划结果"]
  • 写路径

    1. 引擎算出新数据文件 / delete 文件;
    2. 写新的 Manifest(含统计)→ 写 Manifest List → 写新 Metadata JSON(追加 snapshot);
    3. 通过 Catalog 做原子指针切换(乐观锁校验 base snapshot 未变,否则重试)。
  • 读路径

    1. 问 Catalog 拿 metadata.json;
    2. 取 current snapshot → manifest list → 并行读 manifest;
    3. 用 partition bound + 列 min/max 做文件裁剪,绝不扫全表目录;
    4. 对 V2/V3 表合并 delete file / deletion vector 得到可见行集。
  • 时间旅行SELECT * FROM t FOR SYSTEM_VERSION AS OF 123456789AS OF '2026-07-31 10:00:00' 直接选历史 snapshot-id,复用旧 manifest 树。

关键模块(Java 参考实现)

  • iceberg-api:公开 API 与接口;iceberg-core:API 参考实现、Avro 支持;
  • iceberg-parquet / iceberg-orc / iceberg-arrow:文件格式集成;
  • iceberg-spark / iceberg-flink / iceberg-mr:引擎 Datasource V2 / Streaming / Hive InputFormat 集成;
  • iceberg-hive-metastore:HMS Catalog 后端;
  • 周边:pyiceberg(Python 原生)、iceberg-rusticeberg-gopolaris(REST Catalog 服务端)。

Iceberg Catalog 元数据系统 *

  • docker//docker-compose.yml 中定义的 iceberg-rest 容器的镜像 : apache/iceberg-rest-fixture
  • 参考文献:
  • docker-compose.yml
services:
  ...

  # Iceberg REST Catalog
  iceberg-rest:
    image: apache/iceberg-rest-fixture
    hostname: iceberg-rest
    depends_on:
      create-bucket:
        condition: service_completed_successfully
    networks:
      iceberg_net:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8181/v1/config"]
      interval: 5s
      timeout: 3s
      retries: 5
    environment:
      AWS_REGION: us-east-1
      CATALOG_WAREHOUSE: s3://warehouse/
      CATALOG_IO__IMPL: org.apache.iceberg.aws.s3.S3FileIO
      CATALOG_S3_ENDPOINT: http://minio:9000
      CATALOG_S3_ACCESS__KEY__ID: admin
      CATALOG_S3_SECRET__ACCESS__KEY: password

...

iceberg-flink-quickstart 这个 Demo 里,iceberg-rest 容器跑的是 Apache Iceberg REST Catalog 的测试夹具(test fixture),镜像为 apache/iceberg-rest-fixture
它不是一个【生产级 Catalog】(如 Polaris/Nessie/Glue),而是Iceberg官方用来"开箱即跑、验证多引擎互通"的 REST Catalog 参考实现/适配器

iceberge-rest-fixture镜像 (iceberg-rest容器) 是什么

  • 它是 Apache Iceberg 仓库 docker/iceberg-flink-quickstartdocker/iceberg-rest-fixture 构建出的 REST Catalog Adapter Test Fixture——一个把"已有 Iceberg Catalog 实现"包成 WEB HTTP 服务的适配器。

  • docker-compose.yml 里没设 CATALOG_CATALOG__IMPL 的字样,但 apache/iceberg-rest-fixture 镜像的 Dockerfile已经写死了2条 ENV:

  • CATALOG_CATALOG__IMPL=org.apache.iceberg.jdbc.JdbcCatalog
  • CATALOG_URI=jdbc:sqlite::memory: (默认: 内存版SQLLite)
  • 本质:Jetty/Jersey 起的 HTTP 服务 + 内部套一个 JdbcCatalog + 连 SQLite 内存库

一个 HTTP 服务,实现了 https://iceberg.apache.org/docs/latest/rest-catalog(/v1/config/v1/namespaces/v1/namespaces/{ns}/tables/{table} 等端点)。

  • 角色:在 Flink SQL 里你配的 'catalog-impl'='org.apache.iceberg.rest.RESTCatalog' + 'uri'='http://iceberg-rest:8181',客户端就是跟它对话;它再转交给背后的【真正元数据存储】(默认内存 SQLite JdbcCatalog)。

默认服务名 rest_backend,监听 8181,/v1/config 是咱们 healthcheck 里 curl 的那个端点

  • 官方定位:名字带 fixture,即 "Adapter Test Fixture"——给 Spark/Flink/Trino/PyIceberg 做 REST 协议联通性测试用的【轻量Web服务端】。

起什么作用、提供什么能力

  • 作用:让 Flink SQL等【上层应用程序】基于 catalog-impl=org.apache.iceberg.rest.RESTCatalog 走标准 Iceberg REST Open API 建库建表,而不用 Flink 直接依赖 Hive/JDBC 客户端。协议层解耦多引擎。

  • 能力(HTTP 侧):namespace 增删查、table 注册/加载/commit(乐观锁返 409)、/v1/config 下发 warehouse 与 IO 配置。

  • docker-compose.yml 里那几个 ENV 的含义**(CATALOG_ 前缀剥离后变成 catalog property):

  • CATALOG_WAREHOUSE=s3://warehouse/ → 表数据根路径
  • CATALOG_IO__IMPL=org.apache.iceberg.aws.s3.S3FileIO → 数据文件走 S3FileIO 写 MinIO
  • CATALOG_S3_ENDPOINT=http://minio:9000 + ak/sk → 告诉服务端(以及通过 config 下发给客户端)对象存储怎么连
  • 注意:【元数据】本身不进 S3存储,只【数据文件】进 MinIO;【元数据指针】进 SQLite。
  • 解耦作用:把"【Catalog 客户端逻辑】"从各计算引擎(Flink/Spark/Trino)里抽出来,变成统一 HTTP 协议。【计算引擎侧】只需实现一次 RESTCatalog client,不用再为每个后端(Hive/JDBC/Glue)写 connector。

  • 对外提供的能力(HTTP API)

  • Namespace 管理:CREATE DATABASEPOST /v1/namespaces
  • Table 注册与发现:CREATE TABLE / SHOW TABLESPOST /v1/namespaces/{ns}/tablesGET .../tables
  • 加载表元数据(返回 metadata.json 位置、snapshot 指针):GET .../tables/{table}
  • Commit 原子更新(乐观锁:带 requirement 比对,冲突返 409):POST .../tables/{table}
  • /v1/config 下发 warehouse、vended credential 等
  • 可选 OAuth token 端点(fixture 默认不强制开鉴权)

在 quickstart 里具体承载:Flink 建 iceberg_catalog.nyc.taxis → 发 REST 请求给 8181 → iceberge-rest-fixture 把库表指针存 默认的内存SQLite、把 warehouse=s3://warehouse/ 下数据/metadata 文件交给 MinIO(S3FileIO)。

基于什么组件构建

  • 服务框架:Iceberg 代码库里的 iceberg-rest-catalog / iceberg-open-api 模块(shadowJar 打进镜像),底层用 Jetty(内嵌)+ JAX-RS(Jersey) 起 HTTP 服务,不是 Spring Boot,也不是 Spark。
  • 默认元数据存储:未设 CATALOG_CATALOG__IMPLCATALOG_URI 时,fallback 到 内存版 SQLite 的 JdbcCatalog,DB 文件在容器 tmpfs(如 /tmp/iceberg_catalog.db)。
  • 可插拔后端:通过环境变量可换成 PostgreSQL/MySQL JDBC,或指定 catalog-impl(如 HiveCatalog 适配层);IO 层可配 S3FileIO/HadoopFileIO
  • 镜像构建docker/iceberg-rest-fixture/Dockerfile,基于 Iceberg 主线 Gradle 构建的 shadow jar + 精简 JRE。

Iceberge的内存版SQLLite vs. MinIO 上 <database>/<table>/metadata/*.metadata.json 文件

  • 但为什么我能在 MinIO 上看到 metadata/*.metadata.json 文件?

前面提及的元数据存储在默认的内存SQLLite中,二者到底有何区别?
如果我停止 该 iceberg-flink-quickstart 项目的 iceberg-rest 容器,再重新启动,Flink SQL Client上是否能正常读取到MinIO的库表定义和表数据?

  1. 为何 MinIO 有 metadata.json
    Iceberg 每次写入/提交都会生成表【元数据文件】(记录 schema、快照、数据文件指针等)并存入对象存储。这是 Iceberg 表格式的标准行为,与 Catalog 后端无关。
  1. 二者区别
  • MinIO 中的 metadata.json:是表元数据(Table Metadata),记录表的具体结构、历史和文件清单。
  • 内存 SQLite:是 Catalog 注册表(Registry),仅存储“库/表名 ➡️ 最新 metadata.json 路径”的指针。
    类比:SQLite 是“电话簿”,MinIO 文件是“具体的档案内容”。
  1. 重启 iceberg-rest 后能否读取?
    不能。 容器重启导致【内存 SQLite】 数据清空(失忆),Flink 向 Catalog 询问表位置时,会返回“表不存在”。虽然 MinIO 里的表结构和数据文件物理上还在,但因失去了 Catalog 的“入口指针”,SQL Client 无法正常加载和读取。
  • 在不改变任何组件的情况下,当重启 iceberg-rest 容器后,如何主动让其读取 MinIO 上的库表定义到内存SQLLite,以解决 内存SQLLite失忆的问题?

结论:无法主动恢复。

  • iceberg-rest-fixture 内置的 JdbcCatalog 不具备“扫描对象存储反推 Catalog 注册信息”的能力(即无 Hive Metastore 那种 metastore.warehouse.dir 扫描机制)。
  • 内存 SQLite 只认显式的 CREATE TABLE 写入。重启后指针丢失,MinIO 上的文件对它而言只是“【孤儿文件】”。
  • 唯一可行方案(不改组件):
  • 因为DDL逻辑是幂等的(若 metadata.json 已存在则会复用),这会重新向内存 SQLite 写入指针,从而“唤醒”原有表。
  • 重启 iceberg-rest 容器后,手动在 Flink SQL Client 中重新执行 CREATE CATALOG即可。
//20260731 亲测:无需再 CREATE DATABASE` 和 `CREATE TABLE`
ash-4.4# docker stop iceberg-flink-quickstart-iceberg-rest-1
iceberg-flink-quickstart-iceberg-rest-1
ash-4.4# docker start iceberg-flink-quickstart-iceberg-rest-1
iceberg-flink-quickstart-iceberg-rest-1

ash-4.4# docker exec -it jobmanager ./bin/sql-client.sh
...
    ______ _ _       _       _____  ____  _         _____ _ _            _  BETA   
   |  ____| (_)     | |     / ____|/ __ \| |       / ____| (_)          | |  
   | |__  | |_ _ __ | | __ | (___ | |  | | |      | |    | |_  ___ _ __ | |_ 
   |  __| | | | '_ \| |/ /  \___ \| |  | | |      | |    | | |/ _ \ '_ \| __|
   | |    | | | | | |   <   ____) | |__| | |____  | |____| | |  __/ | | | |_ 
   |_|    |_|_|_| |_|_|\_\ |_____/ \___\_\______|  \_____|_|_|\___|_| |_|\__|
        Welcome! Enter 'HELP;' to list all available commands. 'QUIT;' to exit.
Command history file path: /opt/flink/.flink-sql-history
Flink SQL> SELECT * FROM iceberg_catalog.nyc.taxis;
[ERROR] Could not execute SQL statement. Reason:
org.apache.calcite.sql.validate.SqlValidatorException: Object 'iceberg_catalog' not found

Flink SQL> show catalogs;
+-----------------+
|    catalog name |
+-----------------+
| default_catalog |
+-----------------+
1 row in set

Flink SQL> use catalog iceberg_catalog;
[ERROR] Could not execute SQL statement. Reason:
org.apache.flink.table.catalog.exceptions.CatalogException: A catalog with name [iceberg_catalog] does not exist.

Flink SQL> CREATE CATALOG iceberg_catalog WITH (
>   'type'                 = 'iceberg',
>   'catalog-impl'         = 'org.apache.iceberg.rest.RESTCatalog',
>   'uri'                  = 'http://iceberg-rest:8181',
>   'warehouse'            = 's3://lakehouse/icebgerg/',
>   'io-impl'              = 'org.apache.iceberg.aws.s3.S3FileIO',
>   's3.endpoint'          = 'http://minio:9000',
>   's3.access-key-id'     = 'xxx',
>   's3.secret-access-key' = 'xxx',
>   's3.path-style-access' = 'true'
> );
[INFO] Execute statement succeeded.

Flink SQL> show catalogs;
+-----------------+
|    catalog name |
+-----------------+
| default_catalog |
| iceberg_catalog |
+-----------------+
2 rows in set

Flink SQL> use catalog iceberg_catalog;
[INFO] Execute statement succeeded.

Flink SQL> show databases;
+------------------+
|    database name |
+------------------+
| default_database |
|              nyc |
+------------------+
2 rows in set

Flink SQL> use nyc;
[INFO] Execute statement succeeded.

Flink SQL> show tables;
+------------+
| table name |
+------------+
|      taxis |
+------------+
1 row in set

Flink SQL> exit;

重申结论:不改组件无解,只能重跑 DDL。根治需换 PG 等持久化后端。

使用时需要注意什么(踩坑点)

  1. 元数据不持久化:默认内存 SQLite,容器重启 → namespace/table 注册信息全丢;MinIO 里 parquet/metadata.json 还在,但 Catalog 已"忘了"表。Demo 无所谓,真要用需挂 PostgreSQL 或换 Polaris,并给 SQLite/PG 挂 volume。

  2. 不是生产 Catalog:无多租户、无细粒度权限(OAuth 可接但不带 RBAC)、无 branch/merge 等 Nessie 能力、并发 commit 靠 SQLite 锁不扛高并发。

别上生产:无鉴权、无 RBAC、SQLite 内存不并发、重启即丢;生产换 Polaris/PG 等后端,上一轮已展开。

  1. catalog name 要对齐:fixture 默认服务名 rest_backend,Flink/Trino/PyIceberg 侧建的 catalog 名不一致会找不到,可用 CATALOG_CATALOG_NAME=mycatalog 覆盖。
  2. Flink 侧必须配套配 FileIO 与 S3:quickstart 里 io-impl=S3FileIOs3.endpoint=minio:9000path-style-access=true 缺一不可,否则 REST 层能建表但写数据时报 S3 404/403。
  3. 端口与网络:容器内监听 8181,Flink 容器用服务名 http://iceberg-rest:8181 而非 localhost(localhost 是 Flink 自己)。
  4. 版本匹配apache/iceberg-rest-fixture 镜像 tag 建议与 Flink/Iceberg runtime 大版本对齐(如 Iceberg 1.8 配 fixture 1.8.x),跨大版本 REST API 字段可能有差异。
  5. 日志噪音:默认 INFO 很吵,可 LOG_LEVEL=WARN 或挂 simplelogger.properties 降噪。

简言之:quickstart 里的 iceberg-rest = Jetty 起的 Iceberg REST Catalog 参考实现 + 内存 SQLite 后端 + 对接 MinIO S3FileIO,作用是让 Flink 用标准 HTTP 协议管表,价值在多引擎互通验证;代价是"重启即失忆",只适合 Demo 不适合生产。

方案 协议 元数据存储 凭证下发(vended-credentials) RBAC/多租户 Flink 适配 适用场景
Apache Polaris(Incubating) Iceberg REST PostgreSQL(EclipseLink/JDBC) ✅ 原生 ✅ Principal Role + RBAC ✅ RESTCatalog 多引擎、自运维、vendor-neutral 首选
Snowflake Open Catalog / Dremio Open Catalog​ Iceberg REST 托管 不想运维 Polaris 本身
AWS Glue + DynamoDB Lock​ Glue 私有 RPC(新版带 REST 网关) Glue 托管 + DynamoDB 行锁 经 IAM/Lake Formation IAM 策略 ✅ GlueCatalog 全 AWS、Athena/EMR 强绑定
Project Nessie​ REST + 自研 RocksDB/ Mongo/Postgres 基础 ✅ RESTCatalog 需要 Git 式 branch/多表原子提交
Hive Metastore (HMS)​ Thrift HMS 后端 MySQL/PG 极弱 ✅ HiveCatalog 既有 Hadoop 集群、迁移过渡
JDBC Catalog(PG/MySQL)​ 直连 JDBC 关系库表 ✅ JdbcCatalog 单/低并发、无多引擎、简单生产
Hadoop Catalog​ 文件系统 rename 对象存储 version-hint.text 生产不推荐(对象存储 rename 非原子)
  • 结论:对象存储 + Flink 流式写入的生产场景,REST Catalog 是唯一兼顾"Flink/Spark/Trino 同一套 API、凭证不下发到 Flink TaskManager、可换后端不影响客户端"的路线。

  • 在 Iceberg + Flink + 对象存储(S3/OSS/MinIO 等)​ 的生产架构里,Catalog 的选型原则:

  • 新项目默认走 Iceberg REST Catalog(Polaris / 云厂商托管 REST / Nessie),Flink 侧统一用 RESTCatalog client;
  • 只在"纯 AWS 且不想自运维"时考虑 Glue
  • "已有 HMS 且不出 Hadoop 生态"才留 Hive
  • JDBC Catalog 仅限中小规模或过渡。

3 使用指南

安装部署

CASE:Linux(Spark + Hadoop Catalog 本地最小 demo)

未亲测

# JDK 17/21,Spark 3.5+
export SPARK_VERSION=3.5.3
wget https://archive.apache.org/dist/spark/spark-$SPARK_VERSION/spark-$SPARK_VERSION-bin-hadoop3.tgz
tar -xzf spark-$SPARK_VERSION-bin-hadoop3.tgz

# 启动 spark-sql,挂载 iceberg 运行时
./spark-$SPARK_VERSION-bin-hadoop3/bin/spark-sql \
  --packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.11.0 \
  --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
  --conf spark.sql.catalog.local=org.apache.iceberg.spark.SparkCatalog \
  --conf spark.sql.catalog.local.type=hadoop \
  --conf spark.sql.catalog.local.warehouse=/tmp/iceberg-wh

CASE:Windows

未亲测

  • 不推荐原生 Windows 跑 Spark 写 S3;建议 WSL2 + Ubuntu 按上述 Linux 步骤;
  • 纯 Python 分析可用 pip install pyiceberg,配置 catalog.yaml 指向本地 fs 或 Glue REST,无需 JVM。

Docker(Trino + Iceberg + MinIO 快捷栈)

未亲测

  • 使用 trinodb/trino 镜像,挂载 etc/catalog/iceberg.properties
connector.name=iceberg
iceberg.catalog.type=rest
iceberg.rest-catalog.uri=http://polaris:8181/api/catalog

关键操作

  • 建表(V3 + 隐藏分区)
CREATE TABLE local.db.orders ( -- catalog = local , database = db , table = orders
 id bigint,
 user_id string,
 amount decimal(10,2),
 ts timestamp
) USING iceberg -- 声明这是一个 Iceberg 表,而不是 Spark / Hive 默认表;  其他样例值: USING hive , USING parquet
-- days(ts) 属于 partition transform,不会新增物理列
PARTITIONED BY (days(ts)) -- 作用:按天对 ts 字段做分区 ; days(ts) 是 Iceberg 的分区转换函数(transform); 实际生成的 partition 类似:ts_day=2025-01-15
TBLPROPERTIES ('format-version'='3'); -- 可选值: 1 / 2(目前阶段的主流版本) / 3
  • 插入与时间旅行
INSERT INTO local.db.orders VALUES (1,'u1',9.9, timestamp('2026-07-31 09:00:00') );

SELECT * FROM local.db.orders FOR SYSTEM_VERSION AS OF 1;
  • Schema 演进(不重写)
ALTER TABLE local.db.orders ADD COLUMN coupon string;
ALTER TABLE local.db.orders RENAME COLUMN amount TO total;
  • 行级更新 / 合并
DELETE FROM local.db.orders WHERE id = 1;

MERGE INTO local.db.orders t
  USING (SELECT 1 as id, 'u2' as user_id, 12.0 as total) s
  ON t.id = s.id
  WHEN MATCHED THEN UPDATE SET *
  WHEN NOT MATCHED THEN INSERT *;
  • 分区演变
ALTER TABLE local.db.orders ADD PARTITION FIELD hours(ts);
  • 维护
CALL local.system.rewrite_data_files('db.orders');
CALL local.system.expire_snapshots('db.orders', TIMESTAMP '2026-07-01 00:00:00');
CALL local.system.remove_orphan_files('db.orders');
  • PyIceberg 读

python install pyiceberg

from pyiceberg.catalog import load_catalog
cat = load_catalog("local", **{"type": "sql", "uri": "sqlite:///cat.db"})
tbl = cat.load_table("db.orders")
print(tbl.scan().to_arrow())

Z FAQ for Apache Iceberg

Q: Iceberg 和 Delta Lake / Hudi 到底差在哪?

A: Iceberg 核心是引擎中立的开放 spec + Catalog 抽象,Spark/Trino/Flink/Snowflake 同权;Delta 与 Spark/Databricks 耦合更深(虽 UniForm 可双向);Hudi 主打流式 CDC / 主键 upsert / 索引。【新建开放湖仓】多选 Iceberg,强 CDC 选 Hudi,已深度用 Databricks 可选 Delta。

Q: Iceberg 表“存在哪”? *

  • 【数据文件】在对象存储(S3/OSS/COS)或 HDFS;
  • 【元数据】 JSON/manifest 在同表目录下 metadata/
  • 表名→metadata 的路径映射Catalog(HMS/Glue/REST)里。

三者分离,迁移数据时只要 Catalog 指针可换即可。

Q: 为什么查询变慢了,是不是 Iceberg 不行?

  • 多半是小文件过多 / snapshot 未过期 / delete 文件堆积 / catalog 用了弱一致 hadoop 类型

rewrite_data_files + expire_snapshots + remove_orphan_files,并把 catalog 换为 REST/JDBC;Iceberg 本身规划比 Hive 目录扫描快一个量级。

Q: V2 表和 V3 表怎么选?

  • 新表直接 format-version=3(1.11.0+),享受 Deletion Vector、Variant、Row Lineage;
  • 老 V2 表不必强制升,但行级更新多的表升级后读放大显著下降。

Q: Catalog 用 Hadoop 行不行?

  • 本地 demo 可以;生产不行——hadoop catalog 靠文件覆盖模拟原子,并发写易冲突。
  • 生产用 Polaris / Nessie / Glue / JDBC / 云厂商 REST

Q: 多引擎同时写会乱吗?

  • 不会,只要共享同一个强一致 Catalog 并走 Iceberg 【乐观并发协议】;但不同引擎对 V3 特性支持度不同,写方用到的特性读方必须能解析,否则报错。

Q: Equality Delete/等值删除 vs. Position Delete/位置删除 vs. Deletion Vector/删除向量

维度 Equality Delete (V2) Position Delete (V2, V3 弃用) Deletion Vector (V3+)
标识方式 列值(如 user_id=42) (file_path, pos) 单 data file 的 Roaring Bitmap
writer 是否需要知道行位置 不需要​ 需要 需要
读时开销 类 join,随 delete 文件/行数放大 按 pos 跳过,O(1) 单 bitmap 查位,O(1)
积累后小文件数 无界增长 无界增长 每 data file 至多 1 个
典型生产者 Flink CDC upsert、GDPR 按主键删、业务 DELETE Spark/Trino 已知行号的点删 V3 新表默认
V4 走向 社区提议废弃​ 已被 DV 替代 唯一推荐行删机制

Q: AIGC时代下,4大开源数据湖格式,哪款更有竞争优势?哪款与Python数据生态结合更紧密?—— Apache Iceberg *

  • 把 AIGC 时代(RAG 知识库、Embedding 表、多模态元数据、训练特征回放、Agent 审计快照)作为【场景约束】,四大开源表格式(Iceberg / Delta / Hudi / Paimon)的【竞争格局】和 【Python 亲密度】可以拆成两大结论:
  • 综合竞争优势:Apache Iceberg 已是【开放湖仓事实标准】,AIGC 场景下凭"【引擎中立 + REST Catalog + V3 Variant/Row Lineage + PyIceberg 原生】"拿下最稳的基本盘;Paimon 在 Flink 原生流+湖内向量检索上差异化最强,Hudi 坚守 CDC/upsert 老巢,Delta 守 Databricks 围墙。
  • Python 数据生态结合最紧:Iceberg(【PyIceberg】 + DuckDB 扩展 + Pandas/Ray/Arrow 直通),远超 delta-rs(社区维护)、PyHudi(薄弱)、Paimon(几乎无原生 Python 客户端)。

AIGC 时代四维竞争力对照(2026 视角)

维度 Apache Iceberg Delta Lake Apache Hudi Apache Paimon
治理与中立性 ASF 治、REST Catalog/Polaris/Nessie 标准开放 Linux 基金会但 Databricks 主导,Unity 闭源 ASF 治,Onehouse 商业背靠 ASF 治,阿里/Flink 系
多引擎平权 Spark/Trino/Flink/Dremio/Snowflake/BigQuery/DuckDB 全通 Spark 最优,外部靠 UniForm 转 Iceberg Spark+Flink,Trino 弱 Flink 原生,Spark 次之,其余弱
流/CDC/upsert MoR+DV 够用,非设计原点 CDF+DV,偏 Spark Streaming MoR+record key 索引最强 LSM 原生流写+changelog 最强
AIGC 适配 V3 Variant 存 chunk/metadata、_row_id 做 embedding 外键、DV 删文档不重写、时间旅行复现训练集 无 Variant,array 存 embedding 靠 brute-force 1.2+ 原生 VECTOR/BLOB + ANN 索引 1.4 Lumina DiskANN 湖内向量检索 + BLOB 大对象
向量原生 (交 Lance/Milvus,Iceberg 做底座) 是(Hudi 1.2+) 是(Paimon 1.4+)
Python 客户端 PyIceberg(ASF 官方)、DuckDB iceberg 扩展、delta 无此待遇 delta-rs(社区) PyHudi 薄弱 基本无,靠 Java/Scala

核心判断:

  • "新项目默认 Iceberg" 在 2026 已是默认动作——Snowflake/BigQuery/S3 Tables/Athena/Glue 全站原生 Iceberg,反向兼容其他格式(Delta UniForm、Paimon Iceberg-compat 表面)全是"伪装成 Iceberg 被读",重力方向单向。
  • AIGC 特化需求分裂
    • RAG 知识库底座 + 多引擎共享 + Agent 审计时间旅行 → Iceberg(embedding 列用 array<float> 或 V3 Variant 包 metadata,向量检索外挂 Lance/Milvus,Iceberg 管版本与权限)。
    • Flink 实时灌 embedding + 湖内 ANN + 多模态 BLOB → Paimon 1.4 更省事,不用自己拼 Lance。
    • CDC 业务库镜像给特征平台 → Hudi MoR 仍最便宜。
    • 栈全在 Databricks → Delta 别动,UniForm 开一下对外可读即可。

4个数据湖格式都不是为向量检索设计的——Iceberg/Delta/Hudi 传统三强存 embedding 只是 array<float> 列 + 暴力扫,真要 ANN 要么 【Paimon/Hudi】 的【内置索引】,要么 Iceberg+Lance/Milvus 分层。

Python 生态亲密度:Iceberg 断层领先

  • Iceberg

    • pyiceberg(Apache 官方子项目,非社区 fork):读有 filter pushdown、写 append/overwrite/merge、schema 演进、catalog 对接 REST/JDBC/Glue/SQLAlchemy,不依赖 JVM
    • DuckDB INSTALL iceberg 直接 ATTACH 's3://wh' AS ic TYPE iceberg 后 Pandas DataFrame ↔ Iceberg 双向流动,RAG 脚本里边读边 MERGE embedding 很常见。
    • pandaspyarrowraydaftpolars(通过 pyiceberg/arrow)链路通顺;LlamaIndex 有 Iceberg 集成示例做 RAG 版本化。
    • Rust 实现 iceberg-rust 还反哺 PyO3 绑定,Go 也有 iceberg-go
  • Delta Lake

    • deltalake(delta-rs 的 PyPI 包)能读能写能 vacuum,但非 Databricks 官方维护,Spark 外特性滞后(CDF、liquid cluster 不全),DuckDB 可读但非一等公民。
  • Hudi

    • PyHudi 基本停留在实验层,生产 Python 作业多数还是起 Spark JVM 跑 py4j,纯 Python 直连弱。
  • Paimon

    • 没有成熟独立 Python 客户端,Flink/Spark 是入口;做 Python 数据科学要绕道 Spark Pandas UDF 或把 Paimon 当 Iceberg-compat 表用 PyIceberg 读(只读不写稳)。

所以,"Python 数据科学家/ML 工程师单机到湖仓"最顺的链路是:PyIceberg 写元数据 → Parquet 落 S3 → DuckDB 本地 OLAP → Pandas 训练 → 回写 Iceberg DV,这条链路只有 Iceberg 闭环。

AIGC 选型建议

  • 企业级 开放 Lakehouse + RAG 底座 + 多团队多引擎共治Iceberg V3(Variant 存文档 metadata、_row_id 锚 embedding、DV 做软删、Polaris 做跨云鉴权)。
  • Databricks 内 AIGC 流水线 → Delta + UniForm,别为"开放"硬迁。
  • Flink 流灌向量 + 要湖内 ANN + 多模态 BLOB → Paimon 1.4(Lumina 索引省一套外挂)。
  • 业务库 CDC → 特征/标签实时更新 → Hudi MoR 仍是最优解。
  • 纯 Python Notebook 玩 embedding 表 → Iceberg + PyIceberg + DuckDB,不碰 JVM。

趋势层面:V4 Iceberg 在推 single-file commit / relative path / column families(单列族刷新 embedding 不重写整文件),进一步把"AI 表"的维护成本压下来;而 Delta/Hudi/Paimon 都在不同形式"说 Iceberg 语言",标准收敛方向已无悬念

Y 推荐文献

X 参考文献

注:版本号与社区动态以 2026-07 为基准;V4 仍为提案态,生产请以 1.11.x + format-version 3 为准。

posted @ 2026-07-31 16:40  千千寰宇  阅读(15)  评论(0)    收藏  举报