从数据湖到湖仓一体:Iceberg 到底解决了什么问题? 1. 引言一个常见的困惑数据湖、数据仓库、湖仓一体、Iceberg、Hudi、Delta Lake……这些词经常混在一起出现。很多人第一次接触时会有几个典型疑问湖仓一体到底比数据仓库强在哪Iceberg 是什么它不是计算引擎也不是存储那它到底管什么为什么说 Iceberg 是“开放表格式”开放在哪分区演进为什么可以不重写数据多个引擎真的能同时读写同一张表吗这篇文章试图用一条完整的逻辑线把这些问题串起来。本文基于一次从零开始的 Spark Iceberg 实践梳理湖仓一体的核心逻辑适合对数据湖、数据仓库有基本了解但对“湖仓一体”和“Iceberg”仍感到模糊的读者。2. 数据仓库与数据湖各自的困境数据仓库Schema-on-Write 的代价数据仓库的核心模式是Schema-on-Write先定义好表结构再写入数据。它擅长高性能 BI 报表但代价是存储成本高通常使用专有存储只能处理结构化数据格式变更困难数据孤岛严重不同部门各建各的仓。数据湖Schema-on-Read 的代价数据湖的核心模式是Schema-on-Read先把数据存下来用的时候再定义结构。它便宜、灵活能存结构化、半结构化和非结构化数据但问题也很明显没有 ACID并发写容易脏读没有 Schema 管理容易变成“数据沼泽”查询性能差引擎只能靠目录猜容易被某个厂商或引擎锁死。于是就有了一个自然的想法能不能既保留数据湖的低成本和灵活性又拥有数据仓库的管理能力和性能这就是湖仓一体。3. 湖仓一体不是叠加是融合很多人把湖仓一体理解成“数据湖 数据仓库”好像先建一个湖再在湖上建一个仓。但更准确的说法是湖仓一体 开放存储 开放表格式 统一元数据 分层建模 多引擎消费它不是两个系统拼在一起而是用同一份数据、同一套元数据同时满足湖的灵活性和仓的规范性。传统做法是数据湖存原始数据数仓另存一份加工后的数据两份数据、两套元数据、反复搬运。湖仓一体则让原始数据、清洗数据、指标数据都在同一套表体系里只是分层不同。BI 查 Gold 层数据科学查 Bronze 层互不干扰但共享同一份底层数据和元数据。所以更严谨的表述是数据湖通过 Iceberg 等表格式获得了数仓能力再通过分层建模形成数仓结构最终实现湖仓一体。4. Iceberg数据湖的“表操作系统”Iceberg 最准确的定位是一种开放表格式也就是一套公开的规范用来把对象存储上的一堆文件组织成一张有结构、有版本、可事务、可多引擎读写的“表”。它不存实际数据只存元数据。实际数据仍然是 Parquet/ORC放在 S3/OSS/HDFS 上。可以用一个类比Parquet 是“纸”S3/OSS/HDFS 是“仓库”Iceberg 是“图书馆的目录系统 借阅规则”。“开放”体现在三个层面数据文件开放底层是 Parquet、ORC、Avro不锁死元数据规范开放Catalog、元数据 JSON、Manifest 都是公开规范谁都能实现多引擎互操作Spark、Flink、Trino、Hive、Doris 等都可以读写同一张 Iceberg 表。Iceberg 解决了数据湖的哪些痛点没有 Iceberg有 Iceberg数据湖就是文件堆引擎靠目录猜有明确的表、Schema、分区并发写容易脏读、冲突ACID 事务、快照隔离加列、改列名危险Schema 演进安全分区靠目录改分区要重写数据隐藏分区、分区演进只能一个引擎用容易锁死多引擎互操作查历史版本很难时间旅行查任意快照5. 深入理解分区演进为什么不用重写数据这是 Iceberg 最容易被低估的特性之一。要理解它先看传统 Hive 的做法。传统 Hive分区 目录名taxis/ ├── vendor_id1/ │ └── part-001.parquet └── vendor_id2/ └── part-002.parquet分区列直接体现在目录名里。如果你想把分区从vendor_id改成trip_id只能把所有数据读出来重新按新分区写一遍。数据量大时这是一次全量重写。Iceberg分区 元数据里的规则Iceberg 的数据文件路径长这样data/ ├── 00000-0-abc123.parquet ├── 00000-0-def456.parquet └── 00000-0-ghi789.parquet你看不出分区是什么。分区信息记录在元数据里metadata.json里有一个partition-spec说明“当前按 vendor_id 分区”每个 manifest 里记录了它包含的数据文件属于哪个分区每个数据文件自己也知道自己属于哪个分区。当你执行ALTERTABLEdemo.nyc.taxisDROPPARTITIONFIELD vendor_id;ALTERTABLEdemo.nyc.taxisADDPARTITIONFIELD bucket(4,trip_id);Iceberg 只做了一件事在元数据里新增了一个partition-spec把“当前分区规则”指向新规则。旧数据文件一个都没动仍然躺在原来的位置仍然记录着“我属于 vendor_id1”这个旧分区信息。新写入的数据按新规则分区生成新的数据文件和新的 manifest。查询时引擎读元数据拿到所有partition-spec对每个 manifest 用它自己的 spec 去解释分区值。所以旧数据仍然能被正确理解不会因为分区规则变了就读错。这就是“分区演进数据不重写”的含义分区规则可以改旧数据文件原地不动Iceberg 靠元数据里的多版本 partition-spec 来兼容新旧两种分区方式。6. 底层存储结构metadata、manifest、data跑通 Iceberg 后从对象存储侧看一张表的目录结构大致如下warehouse/ └── demo/ └── nyc/ └── taxis/ ├── metadata/ │ ├── 00000-uuid.metadata.json │ ├── snap-snapshot-id-uuid.avro # manifest list │ ├── uuid-m0.avro # manifest file │ └── version-hint.text └── data/ └── vendor_id1/ └── 00000-0-uuid.parquet └── vendor_id2/ └── 00000-0-uuid.parquet逐层理解metadata.json表的“总目录”包含表结构、分区规则、所有快照列表、当前快照指针snap-*.avromanifest list一个快照对应一个 manifest list列出这次快照包含哪些 manifest 文件*-m0.avromanifest file记录一批数据文件的路径、格式、分区值、每个列的 min/max、null 数量等统计信息data/ 下的 .parquet真正的列式数据文件不可变。这就是 Iceberg 查询快的原因引擎先读 manifest用列统计信息裁掉不需要的文件再读 Parquet。Iceberg 还把这些元数据暴露成了虚拟表可以直接 SQL 查spark.read.format(iceberg).load(demo.nyc.taxis.snapshots).show()spark.read.format(iceberg).load(demo.nyc.taxis.files).show()spark.read.format(iceberg).load(demo.nyc.taxis.manifests).show()spark.read.format(iceberg).load(demo.nyc.taxis.history).show()7. 多引擎共享同一张表Iceberg 支持多个引擎同时连接同一个 Catalog访问同一批表。但要注意不是 Iceberg 主动去连接多个引擎而是多个计算引擎可以同时连接同一个 Iceberg Catalog访问同一批 Iceberg 表。典型架构Spark ─┐ Flink ─┤ Trino ─┼──→ 同一个 Iceberg Catalog ──→ 同一批 Iceberg 表 ──→ S3/OSS/HDFS Doris ─┤ Hive ─┘比如一张orders表Flink 实时 CDC 写入Spark 做批量 ETL 加工Trino 做交互式查询Doris 做 BI 报表加速Hive 兼容老 SQL 任务。多引擎同时读完全没问题每个读请求拿到一个快照读期间即使有写入也不会看到未提交的数据。多引擎同时写也支持但依赖 Catalog 的并发协调冲突时会重试或失败。不同 Catalog 的并发能力不同Catalog多引擎并发写能力REST Catalog推荐服务端可协调提交Hive Metastore支持但锁机制较重AWS Glue乐观锁适合云上多引擎JDBC Catalog简单场景可用高并发一般8. 湖仓一体的分层建模Bronze/Silver/Gold湖仓一体通常按“青铜 → 白银 → 黄金”的层级逐步加工奖章架构传统数仓分层说明Bronze 青铜ODS贴源、原始、保留历史。可以存原始 JSON、日志、CDC 变更流甚至不强制 Schema。Silver 白银DWD DWS清洗、去重、格式统一、关联整合、轻度汇总。Gold 黄金ADS 数据集市面向报表、指标、BI、机器学习等消费场景。关键区别数据形态ODS 通常已经是结构化表Bronze 可以更原始建模方式传统数仓有明确范式Silver 更灵活加工方式传统数仓偏 ETL湖仓偏 ELT存储与格式传统数仓多用专有存储湖仓里三层通常都是 Iceberg/Parquet消费对象ADS 主要面向 BIGold 还可以直接服务数据科学和机器学习。一句话奖章架构可以理解为湖仓版的分层建模用同一套 Iceberg 表体系承载从原始到指标的全过程。9. 总结回到开头的几个问题湖仓一体比数据仓库强在哪它保留了数据湖的低成本与灵活性同时通过 Iceberg 获得了数仓的 ACID、Schema 管理和查询性能。Iceberg 是什么一种开放表格式是数据湖的“表操作系统”只管理元数据不存数据。开放在哪数据文件、元数据规范、多引擎互操作三个层面都开放。分区演进为什么不重写数据分区规则存在元数据里旧文件原地不动靠多版本 partition-spec 兼容。多引擎真的能同时读写吗能。读天然并发写依赖 Catalog 协调。从数据湖到湖仓一体本质上是用一套开放的表格式把“湖的灵活”和“仓的规范”统一到同一份数据之上。Iceberg 正是这条路上的关键一环。10. 致谢最后特别感谢DeepSeek在学习和实践过程中的帮助。从理解湖仓一体的概念到梳理 Iceberg 的底层原理再到跑通 Spark Iceberg 的完整实践DeepSeek 都提供了清晰、耐心的讲解和指导让这条从数据湖到湖仓一体的学习路径顺畅了许多。感谢 DeepSeek 帮助学习