第11章 数据湖与湖仓¶
同一个经营问题隔一周重跑,SQL 和模型都没变,结果却变了。排查后发现,底层明细被补数覆盖,而平台只知道“当前目录里有哪些 Parquet 文件”,无法回答上次运行到底读取了哪一版数据。
Agent 平台需要的不只是低成本存储,而是能够把一次回答绑定到明确数据版本的事实底座。 湖仓通过开放表格式、快照、事务、Schema 演化和时间旅行,把对象存储中的文件组织成可引用、可回放、可审计的表资产。
对象存储解决“放得下”,湖仓进一步解决“读得准、查得回、变更可解释”。对 DataAgent 来说,后者往往比单纯节省存储成本更重要。
11.1 从文件堆到可治理表:湖仓的四层边界¶
数据湖擅长保存日志、明细、文件和历史归档;传统数仓擅长事务、建模、权限和稳定查询。湖仓的核心不是把两者名称拼在一起,而是在开放存储之上增加稳定的表语义。
图11-1:湖仓把低成本存储和可治理表语义放在同一底座。来源:本书自绘。Alt text:底层是对象存储提供的低成本文件,上层叠加开放表格式提供的事务与 schema 语义,两层合为同一底座,左侧标注成本优势、右侧标注治理能力。
对象存储只知道文件在哪里,不知道哪些文件共同构成一次事务,也不知道当前 Schema 和有效快照。“有一堆文件”和“有一张表”之间,差的正是事务、版本和元数据语义。
湖仓通常包含四层:存储、表格式、Catalog 和计算引擎。
表11-1:存储层、表格式层、计算引擎层的职责与边界。来源:本书整理。
| 层次 | 职责 | 典型对象 | 与相邻层的区别 |
|---|---|---|---|
| 存储层 | 保存物理文件 | 对象存储、HDFS、Parquet、ORC | 只提供文件读写,不理解表事务 |
| 表格式层 | 管理快照、事务、Schema、分区和文件清单 | Iceberg、Hudi、Delta Lake、Paimon | 定义表语义,不负责所有查询优化 |
| Catalog 层 | 登记表名、命名空间、权限和元数据位置 | Hive Metastore、REST Catalog 等 | 解决发现和治理,不直接保存所有数据文件 |
| 计算层 | 读取和写入表,执行 SQL 或作业 | Spark、Flink、Trino、Doris、StarRocks、DuckDB | 执行计算,不应独占数据资产 |
图11-2:存储、表格式、Catalog 与计算引擎解耦。来源:本书自绘。Alt text:四个可独立替换的方块,对象存储、开放表格式、Catalog、计算引擎,用接口线相连,表示任一层可独立升级而不影响其他层。
这种解耦让同一张表可以被 Spark 写入、Trino 探索、StarRocks 加速,DataAgent 再通过语义层查询。真正要统一的是表资产、权限和版本控制面,而不是强迫所有工作负载使用同一引擎。
11.2 湖仓为什么能支撑 Agent 的可复现回答¶
湖仓最重要的能力可以收敛为六项:ACID、快照、时间旅行、Schema 演化、分区演化和 Compaction。
表11-2:ACID、时间旅行等湖仓核心能力对 DataAgent 的价值。来源:本书整理。
| 能力 | 含义 | 对 DataAgent 的价值 |
|---|---|---|
| ACID | 多文件提交要么全部可见,要么全部不可见 | 避免 Agent 读到半提交数据 |
| 快照 | 每次提交形成稳定版本 | 回答可复现,可固定查询版本 |
| 时间旅行 | 按历史快照或时间点读取 | 审计历史回答和回放事故 |
| Schema 演化 | 字段新增、改名、类型变化有规则 | 识别字段变化和影响范围 |
| 分区演化 | 分区策略可随业务增长调整 | 避免早期分区设计锁死长期查询 |
| Compaction | 合并小文件、整理数据布局 | 降低 OLAP 查询成本和延迟 |
图11-3:湖仓核心能力服务可复现回答。来源:本书自绘。Alt text:ACID 提交、快照、时间旅行三项能力指向同一目标"同一查询在同一快照上结果可复现",说明这些能力共同支撑 Agent 回答可复查。
一次 DataAgent 回答最好记录 catalog、table、snapshot_id、schema_version 和执行时间。这样补数产生新快照后,历史回答仍然有自己的证据版本,不会被“当前数据”覆盖。
这里有三个常见误区。第一,对象存储不等于湖仓,没有表格式和 Catalog 时,它仍只是文件系统。第二,开放表格式不自动等于高性能,查询性能还依赖统计信息、分区、文件大小、排序和第12章的执行引擎。第三,表格式不能只为某个短期工具服务,它会成为核心数据资产的长期契约。
11.3 Iceberg、Hudi、Delta 与 Paimon:从写入模式选表格式¶
开放表格式没有绝对赢家。选择时应先看主要写入模式、计算生态和团队运维能力。
表11-3:Iceberg、Hudi、Delta Lake 三种开放表格式的优势、代价与适用场景。来源:本书整理。
| 方案 | 优势 | 代价 | 适用场景 | 本书建议 |
|---|---|---|---|---|
| Iceberg | 快照、Schema/分区演化和多引擎生态成熟,REST Catalog 路线清晰 | 流式更新和增量消费需结合引擎评估 | 多引擎共享湖仓、长期开放资产 | mini-platform 默认表格式 |
| Hudi | Upsert、增量拉取和流批一体经验丰富 | 表服务和参数较多,运维复杂 | CDC 入湖、近实时更新、增量消费 | 适合高频 upsert 链路 |
| Delta Lake | 与 Spark/Databricks 生态结合紧密,事务体验好 | 非 Databricks 环境需逐项确认兼容性 | 已采用 Databricks 或 Spark 主平台 | 平台绑定场景可优先 |
| Paimon | 面向流式湖仓和实时更新,适合 Flink 生态 | 多引擎生态需按版本验证 | Flink 实时链路、流批一体表 | 实时场景重点评估 |
图11-4:开放表格式选择取决于写入模式和引擎生态。来源:本书自绘。Alt text:二维矩阵以"写入模式(append/upsert/流式)"和"引擎生态广度"为轴,把 Iceberg、Delta、Hudi 落入不同区域,给出选型指引。
如果大量表是批量写入、跨引擎读取和长期归档,Iceberg 通常更自然;如果主要是 CDC 高频 upsert 和增量消费,Hudi/Paimon 更值得评估;如果组织已深度绑定 Databricks/Spark,Delta 可以降低集成成本。
表格式能力越强,越需要匹配相应的表服务能力。 高频 upsert 没有 Compaction、清理和恢复策略,很快会被小文件、删除标记和元数据膨胀拖垮。
11.4 Catalog、Manifest 与读写路径:版本一致性从元数据开始¶
湖仓查询并不是列出目录下全部文件。引擎先从 Catalog 找到表元数据,再读取快照和 Manifest,最后根据分区和统计信息裁剪真正需要扫描的数据文件。
表11-4:Catalog、Manifest、元数据文件等表管理组件的职责与失败模式。来源:本书整理。
| 组件 | 职责 | 输入 | 输出 | 失败模式 |
|---|---|---|---|---|
| Catalog | 管理表名、命名空间、权限和元数据位置 | 表名、身份、操作类型 | 元数据入口、权限结果 | 同名不同表、权限漂移、不可用 |
| Metadata File | 记录 Schema、分区、快照列表 | 提交、Schema 变更 | 当前表元数据 | 元数据版本过多、提交冲突 |
| Manifest / File List | 记录快照包含的数据文件和统计 | 数据文件、分区、统计信息 | 可裁剪文件清单 | 小文件多、统计缺失 |
| Data File | 保存实际业务数据 | Parquet、ORC | 列式数据 | 文件损坏、布局不佳、孤儿文件 |
| Snapshot | 固定一次提交后的可见文件集合 | commit id、时间戳 | 稳定读版本 | 读写版本不一致、过早过期 |
图11-5:一次湖仓查询先读元数据再读数据文件。来源:本书自绘。Alt text:查询流程从 Catalog 定位表,到读 Manifest 元数据做分区/文件裁剪,再只读命中的数据文件,箭头体现"先元数据后数据"减少扫描量。
面向 Agent 的最小表契约可以写成:
{
"table": "dwd.orders",
"table_format": "iceberg",
"catalog": "demo",
"snapshot_id": "742",
"primary_key": ["order_id"],
"partition_fields": ["order_date"],
"schema_version": "orders.v7",
"data_freshness_seconds": 60,
"time_travel_enabled": true
}
写入:先写文件,再原子提交¶
湖仓写入应先在 staging 生成数据文件,准备好统计信息和校验后,再通过表格式事务提交为新快照。这样读者要么看到旧版本,要么看到新版本,不会看到半成品。
图11-6:湖仓写入路径从 staging 到原子提交。来源:本书自绘。Alt text:写入流程先把数据写入 staging 文件,再生成新快照并原子切换 Catalog 指针,箭头表示提交前下游始终读到旧快照、提交后整体可见。
表11-5:Append 与 Upsert 两类写入模式的优势、代价与适用场景。来源:本书整理。
| 方案 | 优势 | 代价 | 适用场景 | 本书建议 |
|---|---|---|---|---|
| Append 表 | 写入简单、审计友好、完整保留事件 | 查询最新状态需窗口或聚合 | 行为日志、审计日志、changelog | 原始层优先 append |
| Upsert 表 | 查询最新状态简单 | 需要主键、版本和删除语义 | 订单、库存、客户 current 表 | 服务 Agent 的明细层常用 |
表11-6:批量写入与即时写入在可见性与小文件成本上的取舍。来源:本书整理。
| 方案 | 优势 | 代价 | 适用场景 | 本书建议 |
|---|---|---|---|---|
| 即时写入 | 数据更快可见 | 小文件多,查询成本上升 | 低吞吐、低延迟链路 | 关键表可用,但监控文件数 |
| 异步 Compaction | 查询更稳定,文件布局更好 | 整理存在延迟 | 高频写入、CDC 入湖 | 生产默认需要表服务 |
读取:先固定版本,再优化扫描¶
一次回答中的多次查询应固定到同一快照;随后才使用分区裁剪、谓词下推和列式统计减少扫描。
图11-7:湖仓读取路径用快照和裁剪控制成本。来源:本书自绘。Alt text:读取路径标出按快照锁定版本、按分区裁剪、按列裁剪、按文件统计跳过四道过滤,逐步缩小实际扫描的数据量。
对 Agent 而言,固定快照首先是正确性问题,其次才是性能问题。 多轮对话如果先后读到不同快照,订单总数、异常门店和供应商归因可能在同一回答中互相矛盾。
11.5 湖仓治理:权限、生命周期、快照与成本要围绕表资产¶
多引擎环境里,权限不能只靠对象存储目录,也不能只配置在某一个查询引擎内部。表级、列级、行级权限、PII、快照保留、生命周期和审计应围绕 Catalog 与表契约统一发布。
图11-8:湖仓治理控制点围绕 Catalog 和表契约展开。来源:本书自绘。Alt text:以 Catalog 为中心,向外辐射权限、生命周期、审计、分层、成本五个治理控制点,表示治理统一挂在 Catalog 与表契约上。
表11-7:权限、生命周期、审计等湖仓治理对象的控制点与缺失风险。来源:本书整理。
| 治理对象 | 推荐控制点 | 缺失后的风险 |
|---|---|---|
| 权限 | Catalog 授权、行列级策略、引擎权限对账 | 不同引擎看到不同数据 |
| 生命周期 | 原始层、明细层、汇总层分层保留 | 存储失控或历史不可追溯 |
| 快照 | 按表价值定义保留窗口 | 历史回答无法复现 |
| PII | 字段标签、脱敏策略、审计 | Agent 泄露敏感信息 |
| 成本 | 文件大小、分区、查询扫描量归因 | 存储和计算费用不可控 |
| 审计 | 记录提交、读取、删除和权限变更 | 事故无法定位影响范围 |
快照保留策略要和回答复现要求一起设计。经营分析、财务复盘或合规问答使用的核心表,应保留足够长的快照或元数据归档;沙箱和探索表可以采用更短生命周期。
11.6 mini-platform 与生产准入¶
mini-platform 先用最小契约表达“什么样的湖仓表适合被 Agent 使用”,而不直接连接真实 Iceberg Catalog。
- 入口:
mini-platform/infra/lakehouse/__init__.py - 核心实现:
mini-platform/infra/lakehouse/table_contract.py - 测试:
mini-platform/tests/test_lakehouse_table_contract.py - 运行入口:
mini-platform/projects/11-lakehouse-contract/run.py
@dataclass(frozen=True)
class LakehouseTableContract:
table: str
table_format: TableFormat
catalog: str
snapshot_id: str
primary_key: tuple[str, ...]
partition_fields: tuple[str, ...]
schema_version: str
data_freshness_seconds: int
time_travel_enabled: bool
validate_agent_readiness 用主键、分区、新鲜度和时间旅行做最小准入检查。生产环境还应补齐权限、PII、质量状态和 Owner。
湖仓表进入自然语言问数前,至少需要明确:统一 Catalog、表格式、快照窗口、Schema 兼容策略、分区、Compaction、权限、PII、生命周期、审计、成本和灾备。常见高风险事故包括直接按对象存储路径删除数据文件、快照清理过快、小文件失控以及不同引擎维护不同 Catalog。
Schema、主键、分区和权限变化都要做 Agent 影响评估。变更前检查语义层绑定、查询样本、报告和评测集;变更后回放高频问题。退役旧表时也不能只删除数据,应先停止新流量并保留历史证据所需的元数据。
“表能被 SQL 查询”只是技术可达,“表适合被 Agent 自动使用”还需要版本、语义、质量和权限都可解释。
本章小结¶
湖仓的核心价值,是在开放存储上补齐事务、快照、Schema、分区和治理语义,让数据不只是“可读”,而是可引用、可复现、可回放。
Iceberg、Hudi、Delta Lake 和 Paimon 应根据写入模式、引擎生态和团队能力选择。DataAgent 查询时不能只记录表名,还应绑定 Catalog、快照、Schema、新鲜度和权限。湖仓因此不是 Agent 的后台存储,而是历史回答能够被证明的事实版本系统。
参考文献¶
Apache Iceberg. (n.d.). Documentation.
Delta Lake. (n.d.). Documentation.
Apache Hudi. (n.d.). Documentation.
Apache Paimon. (n.d.). Documentation.







