Appearance
存储与表格式选型
这一层回答三个问题:数据放在哪(对象存储)、怎么组织成表(Iceberg)、表在哪(Catalog)。
一、为什么是对象存储
三条被淘汰的路
| 方案 | 问题 |
|---|---|
| 关系型数据库直接存 | 数据量一大就性能崩溃,且无法被 Spark/Flink 直接当数据源 |
| HDFS | 存储节点与计算节点耦合,扩存储必须扩计算;深度绑定 Hadoop 生态 |
| NAS / 本地盘 / 块存储 | 无法被计算引擎按对象方式访问,没有 S3 语义,扩不了、共享不了 |
对象存储是当前湖仓的事实标准:Spark、Flink、Doris、Iceberg 都支持 S3 协议。选它不是为了"新",是因为计算与存储解耦这件事只有对象存储能干净地做到。
对 S3 语义的硬要求
不是"能传能下"就叫兼容。Iceberg 的提交依赖这些语义,少一条就会丢数据或写坏表:
| 语义 | 为什么需要 | Ceph RGW | SeaweedFS |
|---|---|---|---|
| 读后写强一致 | 提交 metadata.json 后立刻能读到新版本 | ✅ | ✅ |
| 原子覆盖写 | 快照提交靠元数据文件原子替换 | ✅ | ✅ |
| 多段上传 | 大文件(>5GB)必须分片 | ✅ | ✅ |
| 版本控制 | 误删恢复、对象锁前置条件 | ✅ | ⚠️ 能力有限 |
| 生命周期 | 冷数据转储、过期清理 | ✅ | ✅ |
| 对象锁(WORM) | 合规保留,防篡改删除 | ✅ | ❌ |
| 预签名 URL | 引擎/客户端直读,不暴露密钥 | ✅ | ✅ |
结论:如果只要"能跑 Iceberg",SeaweedFS 够用;如果要合规保留 + 企业级运维,必须 Ceph RGW。
实现对比
| 维度 | Rook + Ceph RGW | SeaweedFS | MinIO(开源版) | MinIO(商业版) |
|---|---|---|---|---|
| 许可与可持续性 | Apache-2.0 + LGPL-2.1,社区持续发版 | Apache-2.0,社区维护 | AGPLv3,2026-04 已归档停维护 | 商业订阅,按容量计费 |
| S3 语义完整度 | 完整 S3 语义,含版本、生命周期与对象锁 | 兼容 S3,对象锁缺失 | 兼容 S3,免费档仅单节点 | 视产品与授权而定 |
| 数据保护 | 多副本 + 纠删码 + 故障域分层 | 纠删码,无对象锁 | 纠删码,社区版无合规能力 | 视授权而定 |
| 扩容方式 | 存储节点池独立扩容,不牵动计算 | 可扩,但需自建运维体系 | 多节点需商业授权 | 按容量订阅 |
| K8s 集成 | 官方 Operator:部署、扩缩容、滚动升级、自愈 | 需自行编排与运维 | 官方 Operator,但已无补丁 | 厂商方案 |
| 大规模验证 | 全球大规模商用,PB 级案例充分 | 企业级案例少于 Ceph | 曾有大量使用,现无安全补丁 | 取决于厂商 |
主推 Rook + Ceph RGW,备选 SeaweedFS。 两者的替换只换存储层,不搬数据、不动上层——这是"数据资产不被锁定"的兜底。
3 节点测试集群的现实
Ceph 的价值在多节点、多故障域、多副本。3 台机器已经能跑 3 OSD、3 副本,比单机有意义得多,但有两个前提:
- 每台有一块可裸用的数据盘(未格式化),Rook 才能建 OSD;
- 愿意花 1.5–2 小时调 OSD、CRUSH、RGW。
如果云厂商只给"已格式化挂载"的数据盘,Ceph 基本没戏;这时用 SeaweedFS 3 节点(master 3 + volume 3 + filer 2 + s3 2)验证 Iceberg 读写,S3 endpoint、AK/SK、bucket 命名都写成配置项,生产切 Ceph 时只改配置。
测试阶段的取舍:
| 选择 | 前提 | 时间 | 验证 |
|---|---|---|---|
| SeaweedFS 3 节点 | 普通盘 | 30–40 min | 分布式 S3、多节点读写 |
| Rook + Ceph RGW | 裸盘 | 1.5–2 h | OSD、CRUSH、3 副本、RGW、对象锁 |
验收记录里要写清楚:哪些 Ceph 能力没有验证(WORM、真实磁盘故障、跨故障域重平衡)。
容量怎么算
| 方式 | 可用容量 | 适用 |
|---|---|---|
| 3 副本 | 裸容量 ÷ 3 | 热数据、小集群、追求简单 |
| 纠删码 4+2 | 裸容量 × 2/3 | 温冷数据、大容量 |
| 纠删码 8+3 | 裸容量 × 8/11 | 归档 |
实际规划再留 20%–30% 余量给快照、小文件、 compaction 临时空间。例:要存 100 TB 原始数据,3 副本至少准备 360 TB 裸盘。
二、为什么是 Iceberg
表格式对比
| 维度 | Iceberg | Hudi | Delta Lake | Hive 表 |
|---|---|---|---|---|
| 多引擎支持 | Spark / Flink / Doris 原生支持,本栈引擎全覆盖 | 以 Spark 为主,流式与多引擎弱于 Iceberg | 多引擎依赖转换层 | 各引擎各自适配,语义不一致 |
| 协议开放性 | 开放规范,表 = 元数据文件 + 数据文件,实现可替换 | 规范与实现耦合较紧 | 规范由单一厂商主导 | 事实标准但技术已过时 |
| 变更能力 | 表级事务 / 快照 / 行级更新删除,历史可追溯 | 以写时复制为主,写放大明显 | 支持 | 只能整分区覆写 |
| 元数据与治理 | 快照与清单文件即结构化元数据,血缘与质量可自动采集 | 元数据开放度一般 | 元数据体系相对封闭 | 元数据与表文件解耦弱 |
| 与对象存储的匹配 | 提交依赖元数据文件原子替换,正好用上 S3 强一致写 | 需额外时间线服务 | 与对象存储组合需调优 | 依赖 HDFS 语义,不宜直接放对象存储 |
| 许可 | Apache-2.0 | Apache-2.0 | Apache-2.0,周边能力与商业版绑定 | Apache-2.0 |
Iceberg 到底规定了什么
一张 Iceberg 表不是"一个目录",而是一棵树:
warehouse/db/orders/
├── metadata/
│ ├── 00001-<uuid>.metadata.json # 表结构、分区、快照列表
│ ├── snap-<id>-1-<uuid>.avro # manifest list
│ └── <uuid>-m0.avro # manifest(记录数据文件与统计)
└── data/
├── 00000-...-00001.parquet
└── ...- metadata.json:表的当前状态(schema、分区规格、快照)。
- manifest list / manifest:每个快照包含哪些数据文件、文件的列统计(min/max、null 数)。
- data files:Parquet/ORC/Avro,按隐藏分区组织。
好处:
- 快照隔离:读写互不阻塞,历史版本可追溯(
VERSION AS OF)。 - 隐藏分区:分区由表定义,查询不用在 SQL 里写分区字段,也不会因为分区列选错而返工。
- 行级变更:
MERGE INTO/DELETE走位置删除或等值删除,CDC 入湖不需要整分区重写。 - 引擎中立:同一张表 Spark 写、Flink 写、Doris 读,元数据不用同步第二份。
结论
Iceberg 是唯一同时满足「多引擎可读写 + 元数据开放 + 行级变更」的表格式。 组件层面没有替代;真正要选的是目录类型。
三、Catalog:表在哪、谁能用
先选协议,再选实现
| 维度 | REST Catalog | Hive Metastore | 引擎各自直连 | 厂商专有目录 |
|---|---|---|---|---|
| 标准与开放 | 开放规范,实现可替换 | 老接口,扩展靠私有约定 | 无统一入口 | 私有协议 |
| 引擎接入 | 各引擎按同一接口接入,新增引擎只改连接串 | 老引擎兼容好,新引擎逐步弃用 | 每引擎各配一份,互不可见 | 需厂商单独适配 |
| 元数据归属 | 集中在 Catalog,落关系库可整体迁移 | 集中在关系库,但技术栈老化 | 分散在各引擎配置里 | 归厂商,导出受限 |
| 权限与治理 | 表级授权与凭证发放收敛到单一入口 | 权限模型弱,靠外部兜 | 权限散落,审计拼不出全貌 | 视厂商实现 |
| 替换成本 | 换 Catalog 只改引擎连接配置 | 迁移需重建元数据 | 每个引擎都要改 | 需厂商迁移服务 |
REST Catalog 把表入口做成开放规范:元数据与权限收敛到一处,入口实现本身仍可替换。
实现对比
| 维度 | Apache Polaris | Nessie | Hive Metastore | JDBC Catalog |
|---|---|---|---|---|
| 与规范的关系 | REST 规范与实现同源 | 以 Git 式分支见长,REST 为附加能力 | 不实现 REST 规范 | 规范里的基础实现 |
| 多引擎支持 | Spark / Flink / Doris 均可直接接入 | 引擎接入面窄于 Polaris | 老引擎兼容好,新引擎弃用 | 功能最简 |
| 权限与治理 | 内置角色授权与表级授权 | 分支能力强,权限模型较弱 | 权限弱,靠外部兜 | 无权限模型 |
| 能力定位 | 通用目录,覆盖本栈全部需求 | 强在数据版本与分支实验 | 存量迁移期可用 | 轻量场景 |
| 成熟度 | 社区活跃 | 成熟度较高但方向偏实验 | 事实标准但技术老化 | 功能有限 |
| 与本栈匹配 | 元数据落 PostgreSQL | 需额外存储与运维 | 需维护独立服务 | 最简,但能力不足 |
主推 Polaris,备选 Nessie。 目录实现替换只改引擎连接配置,不进表数据、不动上层建模,这一层的选择风险最低。
Polaris 的几个概念
| 概念 | 含义 | 建议 |
|---|---|---|
| Realm | 租户隔离单元 | 一个环境一个 realm(dev/prod 分开) |
| Catalog | 一组表 + 存储配置 | 按数据域分(ods/dwd/dws),别只建一个 |
| Namespace | 库 | 对应业务域 |
| Principal / Role | 身份与角色 | 引擎用 service principal,人用 SSO 映射 |
| Credential vending | 向引擎下发短时 S3 凭证 | 引擎不持有长期 AK/SK |
为什么元数据落 PostgreSQL
Catalog 的元数据操作需要事务:创建表、提交快照、改权限,任何一步失败都不能留下半张表。关系库的 ACID 是现成的,PostgreSQL 性能足够,且 Polaris 默认就支持。
MySQL 同样可以,没有本质差异;选 PostgreSQL 主要是团队栈一致 + Polaris 默认路径。
四、落地要点
- Warehouse 布局:
s3://warehouse/<catalog>/<namespace>/<table>/,一个桶装所有表,别一表一桶(桶数量、权限、生命周期都会失控)。 - 路径风格:Ceph RGW 与 SeaweedFS 都支持 path-style,客户端配
s3.path-style-access=true;对外只开 TLS。 - 凭证最小化:每个引擎一个 service principal,只授权到 warehouse 前缀;禁止用 root AK/SK。
- 小文件治理:流式入湖必然产生小文件,必须配三个定期任务:
rewrite_data_files:合并小文件;expire_snapshots:过期快照,默认保留 7 天;remove_orphan_files:清理失败提交留下的孤儿文件。
- 元数据库备份:PostgreSQL 每天全量 + WAL 归档;恢复演练每季度一次。Catalog 挂了,表还在,但"表在哪"没了。
- 容量与配额:bucket 设 quota,防止一个实验把存储写满;对象存储写满比数据库写满更难救。
- 升级路径:Iceberg 格式版本保持 v2;升级 Polaris 前先备份 PG,并确认引擎侧 REST 客户端版本兼容。
结论
| 决策点 | 结论 | 备选 | 换的成本 |
|---|---|---|---|
| 存储形态 | 对象存储(S3) | — | 不可换,是地基 |
| 存储实现 | Rook + Ceph RGW | SeaweedFS | 改 endpoint + 迁数据 |
| 表格式 | Iceberg | — | 换格式 = 重写数据 |
| 目录协议 | REST Catalog | — | 换协议 = 改所有引擎 |
| 目录实现 | Polaris | Nessie | 改连接串,不搬数据 |
| 元数据库 | PostgreSQL | MySQL | 导库 + 改配置 |