湖仓架构基础:数据湖与数据仓库的区别、湖仓一体架构的核心组件、ACID事务在湖仓中的重要性

好,咱们进入第二章。说实话,很多刚接触湖仓的同学,第一反应就是问:数据湖和数据仓库到底有啥区别?为什么非要搞个“湖仓一体”出来?我当年刚转数据架构时也困惑过,后来踩了几个坑才真正理解。

这一章,咱们就把这三个核心问题掰开揉碎讲清楚。

2.1 数据湖 vs 数据仓库:不只是名字不同

先看一个最直观的对比。数据仓库(Data Warehouse)和数据湖(Data Lake)虽然都存数据,但设计哲学完全不同。

维度 数据仓库 数据湖
数据形态 结构化,Schema-on-Write 结构化/半结构化/非结构化,Schema-on-Read
存储格式 行列式(如ORC、Parquet) 原始格式(JSON、CSV、AVRO、图片等)
写入模式 ETL清洗后写入 ELT,先存再处理
事务支持 强ACID 通常无ACID
典型场景 BI报表、固定维度分析 数据探索、机器学习、日志归档
成本 高(专用硬件/软件) 低(廉价对象存储)

你看,数据仓库像是一个精装修的公寓——搬进去就能住,但改格局很麻烦。数据湖则像一块毛坯地皮——你想怎么盖都行,但得自己动手。

我个人的经验是:很多团队一开始图省事,把所有数据都往数据湖里倒。结果半年后,数据湖变成了“数据沼泽”——没人知道哪些数据是干净的、哪些是过期的。反过来,如果一开始就上数据仓库,业务变化快的时候,改一次ETL流程要折腾两周。

所以,单纯的数据湖或数据仓库,都解决不了现代数据架构的全部需求。这就引出了湖仓一体。

2.2 湖仓一体架构(Lakehouse)的核心组件

湖仓一体,说白了就是“既要又要”——既要数据湖的低成本、灵活性,又要数据仓库的ACID、高性能。我参与过几个湖仓项目,总结下来,一个成熟的Lakehouse架构至少包含以下五个核心组件。

湖仓一体(Lakehouse)核心组件架构 底层存储层:对象存储(S3 / ADLS / OSS) 表格式与事务层:Delta Lake / Apache Iceberg / Apache Hudi 计算引擎层:Spark / Presto / Flink / Trino 元数据与目录层:Hive Metastore / AWS Glue / Unity Catalog 接口与消费层:SQL / DataFrame / BI工具 / ML框架 数据流方向

我来逐个拆解一下这些组件,结合我实际项目中的体会。

2.2.1 底层存储层

这是湖仓的基石。通常用廉价的对象存储,比如AWS S3、阿里云OSS、Azure ADLS。为什么不用HDFS?因为对象存储便宜、弹性好、支持跨区域复制。但有个坑——对象存储的最终一致性问题。我曾经遇到过,在S3上写了一个文件,立刻去读,结果读不到。这在湖仓里是致命的,所以需要上层表格式来补偿。

2.2.2 表格式与事务层

这是湖仓一体的灵魂。Delta Lake、Apache Iceberg、Apache Hudi,这三兄弟就是干这个的。它们给对象存储加上了ACID事务、版本控制、Schema演化能力。

  • Delta Lake:Databricks出品,跟Spark绑定较紧,事务日志用JSON存。
  • Apache Iceberg:Netflix开源,社区最活跃,支持多种计算引擎,我目前最看好它。
  • Apache Hudi:Uber开源,擅长增量处理,适合实时场景。

我的建议:如果你的团队Spark生态比较重,选Delta Lake上手快。如果希望引擎中立、未来扩展性强,Iceberg是更稳妥的选择。Hudi适合对实时upsert有强需求的场景。

2.2.3 计算引擎层

湖仓不像传统数仓那样绑定一个计算引擎。你可以用Spark做批处理,Flink做流处理,Presto/Trino做交互式查询。这种存算分离的架构,让资源可以独立扩缩。

2.2.4 元数据与目录层

数据湖里数据多了,没有元数据管理就是灾难。Hive Metastore是经典方案,但现代湖仓更推荐AWS Glue、Unity Catalog这类云原生目录服务。它们能自动发现Schema、管理权限、记录血缘。

2.2.5 接口与消费层

最终用户不关心底层是湖还是仓,他们只需要用SQL、DataFrame或者BI工具能查到数据。湖仓一体要提供标准的JDBC/ODBC接口,让Tableau、PowerBI、Jupyter Notebook都能直接连。

2.3 ACID事务在湖仓中的重要性

好,重点来了。为什么ACID在湖仓里这么重要?

你想想看,传统数据湖为什么被吐槽?因为读的时候数据可能写到一半。比如你正在跑一个报表,另一个任务同时在覆盖同一个分区。结果你读到一半是旧数据,一半是新数据——报表对不上,业务方来找你吵架。

我曾经踩过的坑:有一次做实时ETL,两个Spark任务同时往同一个Hive分区写数据。没有ACID保护,结果分区里出现了重复记录和部分缺失。排查了整整两天,最后发现是并发写入导致的“脏写”。从那以后,我所有湖仓项目都强制要求开启ACID事务。

ACID在湖仓中的具体体现,我列了四个关键点:

  1. 原子性(Atomicity):写入要么全部成功,要么全部回滚。不会出现“写了一半”的中间状态。
  2. 一致性(Consistency):事务前后,数据约束都满足。比如Schema校验、非空约束。
  3. 隔离性(Isolation):并发事务互不干扰。你读你的,我写我的,不会读到脏数据。
  4. 持久性(Durability):事务提交后,数据不会丢。即使系统崩溃,重启后数据还在。

在Delta Lake或Iceberg里,ACID是通过事务日志(Transaction Log)实现的。每次写入都会生成一个新的日志条目,记录这次操作改了哪些文件。读取时,引擎只读取最新快照对应的文件列表。

-- 示例:Delta Lake的ACID写入(PySpark伪代码)
from delta.tables import DeltaTable

# 读取已有表
delta_table = DeltaTable.forPath(spark, "/data/sales")

# 执行upsert操作(原子性保证)
delta_table.alias("updates") \
    .merge(
        source_df.alias("source"),
        "updates.order_id = source.order_id"
    ) \
    .whenMatchedUpdateAll() \
    .whenNotMatchedInsertAll() \
    .execute()

# 如果中途失败,整个事务回滚,不会留下脏数据

你看,代码里没有显式写begin/commit,但底层表格式自动帮你管理了事务边界。这就是湖仓一体比传统数据湖高明的地方。

总结一下:没有ACID的湖,只能叫“数据池塘”。有了ACID,数据湖才能真正承担起企业级数据仓库的职责。这也是为什么我们说“湖仓一体”不是简单的存储叠加,而是架构层面的质变。

嗯,这一章的内容就到这。记住这三个核心概念的区别、组件和ACID的价值,后面讲数据契约的时候,你会频繁用到这些知识。