从数据仓库 → 数据湖 → 湖仓一体的完整演进 · 核心技术 · 代码示例 · 选型指南
标签:
大数据架构IcebergHudiDelta Lake2026 整理
通俗类比: 想象你要管理一家餐厅的食材仓库——
- 数据仓库(数仓) 像一个"精品加工车间":食材进来必须先洗干净、切好、按规格装盘(严格的 ETL 和 Schema),取用方便、品质可控,但存不进"生的、形状怪"的食材(半结构化/非结构化数据),而且加工费很贵。
- 数据湖 像一个"什么都往里扔的大冷库":生鲜、罐头、调料包、甚至不知道是什么的袋子全丢进去,便宜又能装,但时间一长没人知道哪个袋子是什么,变成"数据沼泽",想直接给客人上菜(分析查询)很难。
- 湖仓一体(Lakehouse) = 把大冷库"装上货架、贴上标签、装上温控和出入库系统":既保留数据湖便宜、灵活、什么都能存的优点,又具备数仓的管理能力(事务、版本、高性能 SQL 查询)——一套存储,两种能力。
正式定义: 湖仓一体是一种结合了数据湖的低成本灵活存储与数据仓库的数据管理及分析性能的新一代数据架构。它在对象存储(S3 / OSS / HDFS 等)上的开放文件格式(Parquet/ORC)之上,增加一层元数据与事务管理层(开放表格式),从而让同一个存储上同时支持 BI 报表、SQL 分析、机器学习、实时计算等多种负载。
💡 核心公式(记住这个就够了):
湖仓一体 = 对象存储(便宜) + 开放文件格式(Parquet) + 开放表格式(Iceberg/Hudi/Delta) + 统一计算引擎(Spark/Flink/Trino 等)
典型代表:Oracle、Teradata、以及云上的 Snowflake、Redshift、ClickHouse、阿里云 MaxCompute/Hologres。
典型形态:HDFS / S3 / OSS 上直接堆 CSV、JSON、Parquet 文件,用 Hive/Spark 跑批。
2015~2020 年,很多公司的做法是"湖 + 仓"两套并行:原始数据进湖,ETL 加工后再进仓。结果是:
2020 年 Databricks 提出 Lakehouse 概念并发布论文《Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics》,核心思路是:不要两套系统了,直接在湖的存储上加一层"事务 + 元数据 + 高速读取"的管理层,让湖本身具备仓的能力。 此后 Iceberg、Hudi、Delta Lake 三大开放表格式快速成熟,湖仓一体成为主流架构。
演进路径:
① 传统数仓 ② 湖 + 仓 两套并行 ③ 湖仓一体 Lakehouse
───────────── ───────────────── ─────────────────────
业务库 → 数仓(贵) 数据湖(便宜) ⇄ 数仓 统一计算: SQL / BI / 流 / ML
只存结构化 · 强事务 ↑ 同步链路复杂 开放表格式: Iceberg/Hudi/Delta
存不了日志/图片/ML数据 数据存多份 · 成本高 Parquet + 对象存储(便宜)
一套存储 · 事务 · 版本 · 全场景
湖仓一体典型分层架构:
┌──────────────────────────────────────────────────────────────────────────┐
│ 应用层:BI 报表 · 自助分析 · 数据 API · 机器学习/AI 应用 │
├──────────────────────────────────────────────────────────────────────────┤
│ 计算引擎层:Spark(批) · Flink(流) · Trino/Presto(交互查询) │
│ StarRocks/Doris(OLAP 加速) │
├──────────────────────────────────────────────────────────────────────────┤
│ 表格式/元数据层(核心):Iceberg / Hudi / Delta Lake │
│ —— 事务 · 版本 · Schema 演进 · 索引 │
├──────────────────────────────────────────────────────────────────────────┤
│ 存储层:S3 / OSS / COS / HDFS —— 开放文件格式 Parquet / ORC │
└──────────────────────────────────────────────────────────────────────────┘
| 维度 | 数据仓库 | 数据湖 | 湖仓一体 |
|---|---|---|---|
| 数据类型 | 仅结构化数据 | 结构化 + 半结构化 + 非结构化(全部) | 全部类型 |
| 存储成本 | 高(一体机/专用存储) | 极低(对象存储) | 低(对象存储) |
| Schema | Schema-on-Write(写入时强约束) | Schema-on-Read(读取时解释) | 两者兼有:写入有校验,且可平滑演进 |
| ACID 事务 | ✅ 支持 | ❌ 基本没有 | ✅ 支持(表格式提供) |
| 数据版本/回滚 | 部分支持 | ❌ | ✅ Time Travel 时光旅行 |
| 更新/删除 | ✅ 原生支持 | ❌ 只能整文件覆盖 | ✅ 支持(行级更新/CDC/MOR) |
| BI 查询性能 | ✅ 很好 | ❌ 差 | ✅ 好(配合缓存/索引可接近数仓) |
| 机器学习 | ❌ 不友好 | ✅ 直接读原始数据 | ✅ 直接在湖上训练,无需导出 |
| 流批一体 | 困难 | 困难 | ✅ 天然支持(Flink + 表格式) |
| 开放性 | ❌ 常被厂商锁定 | ✅ 开放文件 | ✅ 完全开放(存储+格式+元数据都可迁移) |
| 典型产品 | Snowflake、Redshift、MaxCompute、ClickHouse | S3/HDFS + Hive + Spark 原始用法 | Databricks、阿里云 EMR+OSS+Paimon/Iceberg、腾讯云 Oceanus+Iceberg、自建 Flink+Iceberg |
⚠️ 常见误区:
- ❌ "湖仓一体 = 数仓 + 数据湖两个产品拼一起" —— 错。它是一套存储上的统一架构,拼装反而是要被淘汰的旧模式。
- ❌ "湖仓一体就是个产品" —— 它更像一种架构理念,可以用开源组件自建,也可以买一体化云产品。
- ❌ "有了湖仓就不需要 OLAP 引擎了" —— 对超高并发的看板场景,仍常配 StarRocks/Doris/ClickHouse 做"查询加速层"(只是数据源变成了湖仓表,不再需要单独同步)。
基于乐观并发控制(OCC)+ 快照隔离:每次提交生成一个新快照(snapshot),提交时校验是否有冲突。就像 Git——大家基于同一版本改,提交时如果版本变了就先解决冲突再提交。
新增列、重命名列、修改列类型都不需要重写全表数据,也不用担心下游任务报错。旧数据文件里没有新列时读出来就是 NULL。
每次写入都是一个快照,可以查"昨天 10 点的表长什么样",也能一键回滚误操作。
-- 查询 1 小时前的数据
SELECT * FROM orders TIMESTAMP AS OF now() - interval 1 hours;
-- 查询快照 id = 12345 时的数据
SELECT * FROM orders VERSION AS OF 12345;
-- 回滚到旧快照(撤销误操作)
CALL catalog.system.rollback_to_snapshot('db.orders', 12345);
传统数据湖只能整文件重写;湖仓格式支持行级更新,可以直接消费 MySQL binlog(CDC 数据),把业务库的增删改实时同步进湖表。这是"实时数仓"的基础。
底层数据就是标准的 Parquet 文件 + 元数据文件,任何支持该表格式的引擎都能读:Spark 跑批、Flink 实时、Trino 即席查询、StarRocks 加速、Python 直接喂给模型训练。避免了厂商锁定和多份数据副本。
开放表格式是湖仓一体的"心脏"。它本质上是一层元数据协议:规定"这个表由哪些数据文件组成、每个文件有哪些统计信息、表当前处于哪个快照",从而在"一堆文件"之上模拟出数据库表的能力。
Catalog ──→ metadata.json ──→ manifest list ──→ manifest 文件
(指向当前元数据) (快照列表) (本次快照的清单) (列统计信息)
│
▼
data-1.parquet data-2.parquet data-0.parquet(虚线)
含 min/max 统计 新写入的数据 旧文件(被更新后标记删除)
写入 = 新增 parquet 文件 + 提交新快照(原子替换 Catalog 指针);查询 = 按当前快照扫描有效文件,用统计信息跳过无关数据。
| 对比项 | Apache Iceberg | Apache Hudi | Delta Lake |
|---|---|---|---|
| 出生 | Netflix 2018 开源,2020 进 Apache | Uber 2017 开源(原名 Titan) | Databricks 2019 开源 |
| 设计初衷 | 为超大规模分析表设计,中立开放 | 为"数据库式"增量入湖/更新设计 | 为 Spark 生态深度集成设计 |
| 更新模式 | Copy-on-Write 为主(v2 支持位置删除) | COW + MOR(Merge-on-Read)双模式,更新能力最强 | COW 为主(新版支持 Deletion Vector 近似 MOR) |
| 擅长场景 | 海量离线分析、批处理、多引擎共存 | 高频 CDC 入湖、近实时更新、Flink 流场景 | Spark 生态、与 Databricks 平台配合 |
| 引擎支持 | 最广泛:Spark/Flink/Trino/StarRocks/Doris 等 | Spark/Flink 为主 | Spark 最佳;Flink/Trino 支持渐好 |
| 隐藏分区/分区演进 | ✅ 强项(按天分区可改为按小时,无需重写) | 支持有限 | 需重写 |
| 国内生态 | 极广(阿里/腾讯/网易等默认推荐之一) | 广泛(Flink 社区常推荐) | 多见于使用 Databricks 或 Azure 的公司 |
| 后起之秀 | Apache Paimon(原 Flink Table Store):流式场景新贵,更新性能好,是 Flink 官方生态主推的流式湖格式,2023 起在国内实时湖仓落地很多 |
💡 一句话选型直觉(详细选型见第 10 章): 偏批/多引擎/中立 → Iceberg;偏 CDC 高频更新/流式 → Hudi 或 Paimon;已深度使用 Databricks → Delta Lake。三者都在快速演进,差距在缩小。
| 层次 | 可选技术(开源为主) |
|---|---|
| 存储层 | AWS S3 / 阿里云 OSS / 腾讯云 COS / Azure ADLS / HDFS / MinIO(自建对象存储) |
| 文件格式 | Parquet(列存,最主流)、ORC、Avro(部分中间数据) |
| 表格式 | Iceberg、Hudi、Delta Lake、Paimon |
| Catalog(元数据服务) | Hive Metastore(最常用)、AWS Glue、Nessie、Polaris、Rest Catalog |
| 批处理引擎 | Spark(事实标准)、Hive MR(老系统) |
| 流处理引擎 | Flink(事实标准)、Spark Structured Streaming、Kafka Connect |
| 交互查询/OLAP 加速 | Trino/Presto、StarRocks、Doris、ClickHouse(做加速层) |
| CDC 采集 | Flink CDC、Debezium、Canal |
| 调度与治理 | DolphinScheduler、Airflow、Dagster;数据质量:Griffin / dbt tests;血缘:OpenLineage |
| 一体化商业平台 | Databricks、Snowflake(Iceberg Tables)、阿里云 EMR/实时计算 Flink 版 + OSS、腾讯云 Oceanus + DLC |
💡 国内最常见的开源组合("实时湖仓"标准栈):
MySQL/业务库 → Flink CDC → Kafka → Flink 写入 Paimon/Iceberg(OSS 上) → Spark 离线加工 → StarRocks/Trino 加速查询 → BI 看板 / AI 应用一套链路同时覆盖实时、离线、机器学习三类需求。
下面用最经典的 Spark + Iceberg 组合,演示从建表、写入、更新、时光旅行到回滚的完整流程。所有 SQL 都能在一个 Spark-SQL 会话里直接跑通。
# 拉起含 Spark + Iceberg 的镜像(也可本机下载 spark + iceberg jar)
docker run -it --rm tabulario/spark-iceberg bin/spark-sql \
--packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.5.2 \
--conf spark.sql.catalog.demo=org.apache.iceberg.spark.SparkCatalog \
--conf spark.sql.catalog.demo.type=hadoop \
--conf spark.sql.catalog.demo.warehouse=s3://my-lakehouse/warehouse/
-- 建表:按天分区(分区只影响裁剪,改分区策略无需重写数据)
CREATE TABLE demo.db.orders (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10,2),
status STRING,
pay_time TIMESTAMP
)
USING iceberg
PARTITIONED BY (days(pay_time));
-- 插入数据(也可以直接写 Parquet 文件路径、或从 Kafka/CDC 流式写入)
INSERT INTO demo.db.orders VALUES
(1001, 88, 199.00, 'PAID', '2026-09-19 10:30:00'),
(1002, 91, 59.90, 'PAID', '2026-09-19 11:05:00'),
(1003, 88, 1299.00,'CREATED', '2026-09-20 09:12:00');
-- 行级 UPDATE:把超时未支付的订单改为关闭
UPDATE demo.db.orders
SET status = 'CLOSED'
WHERE status = 'CREATED'
AND pay_time < current_timestamp() - interval 24 hours;
-- 行级 DELETE:合规要求删除某用户数据(GDPR 场景)
DELETE FROM demo.db.orders WHERE user_id = 88;
-- 查看提交历史
SELECT made_current_at, snapshot_id, operation
FROM demo.db.orders.snapshots
ORDER BY made_current_at;
-- 查询更新前的数据(回看历史)
SELECT * FROM demo.db.orders
VERSION AS OF 5281947119917518575;
-- 回滚到旧快照,撤销刚才的误操作
CALL demo.system.rollback_to_snapshot('demo.db.orders', 5281947119917518575);
-- 新增一列:旧数据文件不用重写,读出为 NULL
ALTER TABLE demo.db.orders
ADD COLUMN coupon_id BIGINT;
-- 把按天分区改成按小时分区?Iceberg 支持分区演进,无需重建表
ALTER TABLE demo.db.orders
SET PARTITION SPEC (hours(pay_time));
-- 把小文件合并成约 512MB 的大文件(流式写入后必做)
CALL demo.system.rewrite_data_files(
table => 'demo.db.orders',
options => map('target-file-size-bytes','536870912')
);
-- 清理 7 天前的过期快照,释放存储(否则历史版本会一直占空间)
CALL demo.system.expire_snapshots(
table => 'demo.db.orders',
older_than => now() - interval 7 days
);
-- Flink SQL:MySQL 订单变更实时入湖(Flink CDC + Iceberg)
CREATE TABLE mysql_orders (
order_id BIGINT, amount DECIMAL(10,2), status STRING,
PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
'connector' = 'mysql-cdc',
'hostname' = 'mysql', 'database-name' = 'shop',
'table-name' = 'orders'
);
INSERT INTO lake_orders SELECT * FROM mysql_orders;
-- 业务库的 INSERT/UPDATE/DELETE 会持续以行级更新方式写入湖表
-- 下游 StarRocks / Trino 查询湖表即可看到近实时的数据
痛点: 离线 T+1 报表 + 实时看板要维护两套链路(Lambda 架构),逻辑写两遍,口径还对不上。
湖仓方案: Flink CDC 把订单库变更实时写入 Paimon/Iceberg 表,实时链路和离线链路读写同一张表(Kappa 思路),StarRocks 直查湖表供大屏使用。
痛点: 特征数据在数仓,原始日志在湖,训练前要人工导出拼接,且无法追溯"模型是用哪个版本的数据训的"。
湖仓方案: 特征表直接建在湖上(Spark/Flink 加工),PyTorch/ML 工程师用 iceberg-spark-runtime 或 pyiceberg 直接按快照读取训练集——每个模型都能记录"训练时用的 snapshot_id",实现数据可复现。
# Python 侧读取湖表快照做训练(pyiceberg + pyarrow)
from pyiceberg.catalog import load_catalog
catalog = load_catalog("demo")
tbl = catalog.load_table("db.user_features")
df = tbl.scan(snapshot_id="5281947119917518575").to_arrow().to_pandas()
# df 直接喂给 sklearn / PyTorch dataloader
痛点: 分析师用 SQL 即席查询,数据科学家跑 Spark,工程师写 Flink——以前数据要复制到三个系统。
湖仓方案: 数据只存一份在 OSS+Iceberg,Trino/StarRocks 提供秒级 SQL,Spark 跑大作业,统一 Catalog + 统一权限(Ranger)管理。
App/前端埋点 JSON 直接以 Parquet 入湖(Schema-on-Write 宽表),平时只算聚合指标;某天想分析"新按钮点击率"时,直接用 SQL 扫原始明细——这是数仓做不到、纯数据湖又慢又乱的场景,湖仓刚好补上。
💡 知名实践参考: Netflix(Iceberg 诞生地,EB 级表)、Uber(Hudi 诞生地,出行数据)、腾讯视频/美团/字节(大规模 Iceberg/Hudi/Paimon 落地)、Adobe/BM(Iceberg 多引擎 BI)。国内云厂商(阿里 EMR、腾讯 Oceanus/DLC、华为 FusionInsight)均已把湖仓一体作为默认推荐架构。
| 坑 | 现象 | 最佳实践 |
|---|---|---|
| 小文件爆炸 | Flink/高频写入后文件数百万级,查询越来越慢,元数据膨胀 | 定期 rewrite_data_files / 开启自动 Compaction;写入端攒批;分区粒度别太细 |
| 快照只增不减 | 存储成本持续上涨,明明"删了"数据但磁盘没释放 | 按业务 SLA 定期 expire_snapshots(如保留 7 天);配合 remove_orphan_files |
| MOR 读放大 | Hudi/Paimon 高频更新后读查询变慢 | 控制 log 文件大小、及时 Compact;对读多写少的表用 COW |
| 分区设计照搬数仓 | 按"省份×日期"分区导致海量小分区 | 湖仓里分区只需支撑裁剪,避免过度分区;利用 Iceberg 隐藏分区和分区演进 |
| 元数据/Catalog 单点 | Hive Metastore 挂了全平台瘫痪 | HMS 高可用部署;大型平台考虑 Nessie/Rest Catalog 多版本管理 |
| 没有做数据质量卡点 | 垃圾数据进湖,下游全脏 | 入湖管道加校验(空值率、主键唯一、值域);用 dbt tests / Great Expectations 做质量门禁 |
| 权限治理缺失 | 任何人都能扫全表,敏感数据裸奔 | 统一 Catalog + Apache Ranger/IAM 做表级/列级权限 + 脱敏 |
| 以为湖仓能替代一切 | 直接拿湖表撑万级 QPS 看板,扛不住 | 超高并发场景加 StarRocks/Doris 查询加速层(湖仓内建或同步),或开缓存 |
是否已深度绑定 Databricks / AWS 生态?
├─ 是 → Delta Lake(Databricks)/ Iceberg(Glue + Athena 也在大力推)
└─ 否 → 看核心负载:
├─ 以离线批 + 多引擎分析为主 → Apache Iceberg(最中立、引擎支持最广)
├─ 以 CDC 高频更新 / 流式为主 → Apache Hudi 或 Apache Paimon(Flink 系首选 Paimon)
├─ 预算充足、想要开箱即用 → 商业平台:Databricks / 阿里云 / 腾讯云一体化湖仓
└─ 不确定 → 先用 Iceberg 起步(社区最大、迁移成本最低),跑通后再演进
| 术语 | 通俗解释 |
|---|---|
| OSS/S3/HDFS | 对象存储/分布式文件系统,湖仓的"地面"——数据文件实际躺在这里,成本极低 |
| Parquet | 列式存储文件格式:同一列数据放一起并压缩,分析查询只读需要的列,又小又快 |
| 开放表格式 | 在文件之上定义"表"的元数据协议(Iceberg/Hudi/Delta/Paimon),提供事务、版本、统计信息 |
| Catalog | "图书馆索引系统":记录每个表名指向哪份元数据,是事务提交的原子替换点 |
| 快照 Snapshot | 表在某一次提交后的完整状态(像 Git commit),Time Travel 的基础 |
| COW(Copy-on-Write) | 更新时重写整个数据文件:读快、写贵,适合读多写少 |
| MOR(Merge-on-Read) | 更新先追加到增量日志,读取时合并:写快、读略慢,适合高频更新 |
| CDC | 变更数据捕获:把数据库的 INSERT/UPDATE/DELETE 以日志方式实时抽出来(如 Flink CDC/Debezium) |
| Compaction / Clustering | 后台把小文件合并成大文件 / 按常用查询列重排数据,是湖仓的"日常保养" |
| Time Travel | 按时间或快照 ID 查询/回滚历史数据 |
| Schema Evolution | 表结构演进:加列/改名/改类型不重写数据 |
| Lambda 架构 | 旧方案:实时链路 + 离线链路两套并行(湖仓一体推动其向 Kappa/流批一体演进) |
湖仓一体知识全解 · 整理于 2026-09 · 内容涵盖概念演进、核心技术、Iceberg/Hudi/Delta/Paimon 对比、可运行示例与选型建议
还没有评论,登录后来说两句。