2. 核心技术栈概览

聊数据湖仓,绕不开这五个组件。它们就像乐高积木里的核心模块——有人负责算,有人负责存,有人负责管。我刚开始接触那会儿,也经常搞混它们各自该干什么。今天咱们就把它们拆开看看。

一句话总结:Spark 和 Flink 是计算引擎,Iceberg、Hudi、Delta Lake 是存储格式。前者决定怎么算,后者决定数据怎么组织、怎么保证一致性。

2.1 计算引擎:Spark 与 Flink

先说计算层。Spark 和 Flink 是目前最主流的两个大数据计算引擎。它们都能处理海量数据,但设计哲学不太一样。

Apache Spark:批处理的王者

Spark 的核心思想是「内存计算」。它把数据加载到内存里,然后做迭代计算。对于批处理任务,比如跑 T+1 报表、做 ETL 清洗,Spark 的表现非常稳定。

我个人习惯用 Spark 做离线数仓的底座。它的 DataFrame API 写起来很顺手,而且生态极其丰富——SQL、MLlib、GraphX 全都有。你想想看,一个引擎能搞定数据清洗、模型训练、图计算,这在以前得用好几个工具。

// Spark 读取 Iceberg 表的示例
val df = spark.read
  .format("iceberg")
  .load("catalog.db.sales_table")

df.groupBy("region")
  .agg(sum("amount").as("total_sales"))
  .show()

我的经验:Spark 的 shuffle 操作是性能瓶颈。我在项目中遇到过因为数据倾斜导致任务跑几个小时的情况。后来加了 salting 和广播变量,才把时间压到 15 分钟。嗯,调优 Spark 其实就是在跟 shuffle 做斗争。

Apache Flink:流处理的标杆

Flink 天生就是为流处理设计的。它的事件时间语义、精确一次保证、状态管理,这些特性让它在实时场景下几乎没有对手。

说白了,如果你要做实时数仓、实时风控、实时大屏,Flink 是首选。它能把数据从 Kafka 里读出来,实时关联、聚合、写入下游,延迟能做到毫秒级。

// Flink 读取 Kafka 并写入 Hudi 的示例
DataStream<String> stream = env
  .addSource(new FlinkKafkaConsumer<>("topic", new SimpleStringSchema(), props));

stream
  .map(new JsonToRowData())
  .sinkTo(HoodieTableSink.create()
    .path("hdfs://warehouse/orders")
    .tableType("COPY_ON_WRITE")
    .build());

避坑指南:我曾经在 Flink 的 checkpoint 上栽过跟头。状态太大导致 checkpoint 超时,任务频繁重启。后来调整了 state backend 和 checkpoint 间隔,才稳定下来。记住:Flink 的状态管理是双刃剑,用好了是利器,用不好是灾难。

2.2 存储格式:Iceberg、Hudi、Delta Lake

这三个东西经常被放在一起比较。它们解决的问题是一样的:让数据湖支持 ACID 事务、时间旅行、增量查询。但实现方式各有千秋。

特性 Apache Iceberg Apache Hudi Delta Lake
出身 Netflix 开源 Uber 开源 Databricks 开源
核心优势 表格式规范、多引擎支持 增量处理、Upsert 性能 与 Spark 深度集成
事务模型 乐观并发控制 基于时间线的 MVCC 乐观并发控制
时间旅行 支持 支持 支持
Schema 演进 支持 支持 支持
主要场景 数据湖仓、多引擎查询 实时入湖、CDC Databricks 生态

Apache Iceberg:表格式的规范派

Iceberg 最让我欣赏的是它的设计哲学——它定义了一套完整的表格式规范。这意味着任何引擎只要实现了这个规范,就能读写 Iceberg 表。Spark、Flink、Trino、Presto 全都支持。

我在项目中用 Iceberg 做统一存储层。上游 Flink 写实时数据,下游 Spark 跑批处理,Trino 做即席查询。所有引擎操作同一份数据,不用来回拷贝。这种体验,说实话,很爽。

-- 在 Spark SQL 中创建 Iceberg 表
CREATE TABLE catalog.db.orders (
  order_id BIGINT,
  user_id STRING,
  amount DOUBLE,
  ts TIMESTAMP
) USING iceberg
PARTITIONED BY (days(ts))

Apache Hudi:增量处理的实战派

Hudi 的强项是增量处理。它支持两种表类型:Copy-On-Write(写时复制)和 Merge-On-Read(读时合并)。前者适合读多写少的场景,后者适合写多读少的场景。

我记得有个实时入湖的项目,数据量每天几亿条,要求分钟级延迟。用 Hudi 的 MOR 表,配合 Flink 写入,效果非常好。它还能自动做小文件合并,省了不少运维功夫。

我的建议:如果你主要做 CDC(变更数据捕获)或者实时入湖,Hudi 是很好的选择。它的 Upsert 性能在三个方案里是最优的。但要注意,Hudi 的配置项比较多,需要花时间理解。

Delta Lake:Spark 生态的原生派

Delta Lake 是 Databricks 推出的,跟 Spark 的集成度最高。如果你用的是 Databricks 平台,Delta Lake 几乎是默认选择。它的 ACID 事务、Schema 强制、数据版本管理,用起来很顺手。

不过 Delta Lake 有个限制——它跟 Spark 绑定得比较紧。虽然现在也支持 Flink 和 Trino,但体验不如 Iceberg 那么自然。如果你团队的技术栈以 Spark 为主,Delta Lake 值得考虑。

-- Delta Lake 的 MERGE 操作
MERGE INTO delta.`/data/sales` AS target
USING updates AS source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET target.amount = source.amount
WHEN NOT MATCHED THEN INSERT *

2.3 核心知识体系

下面这张图是我自己整理的,把五个组件的关系和定位画清楚了。你可以把它当作本章的「地图」。

数据湖仓核心技术栈 Apache Spark(批处理) Apache Flink(流处理) Apache Iceberg Apache Hudi Delta Lake 统一数据湖存储(HDFS / S3 / OSS) 说明: 计算引擎(Spark/Flink)负责数据处理逻辑。 存储格式(Iceberg/Hudi/Delta Lake)负责数据组织与事务保证。 底层统一存储在 HDFS 或对象存储上。

2.4 怎么选?我的建议

很多同学问我:「到底该用哪个?」我的回答是:看场景。

  • 批处理为主,多引擎查询:Iceberg + Spark。Iceberg 的表格式规范让多引擎协作变得简单。
  • 实时入湖,CDC 场景:Hudi + Flink。Hudi 的 Upsert 和增量查询能力是独门绝技。
  • Databricks 生态:Delta Lake。跟 Spark 无缝集成,开箱即用。
  • 流批一体:Flink + Iceberg。Flink 写 Iceberg 表,Spark 读,这是目前比较流行的组合。

注意:不要试图在一个项目里同时用三个存储格式。我见过有人 Iceberg、Hudi、Delta Lake 全用上,结果运维复杂度翻了好几倍。选一个,用透它,比什么都强。

好了,核心技术栈就聊到这儿。记住:工具是死的,场景是活的。理解每个组件的设计初衷,比背 API 重要得多。