2. 核心技术栈概览:Apache Spark、Apache Flink、Apache Iceberg、Apache Hudi、Delta Lake
聊数据湖仓,绕不开这五个组件。它们就像乐高积木里的核心模块——有人负责算,有人负责存,有人负责管。我刚开始接触那会儿,也经常搞混它们各自该干什么。今天咱们就把它们拆开看看。
一句话总结: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 核心知识体系
下面这张图是我自己整理的,把五个组件的关系和定位画清楚了。你可以把它当作本章的「地图」。
2.4 怎么选?我的建议
很多同学问我:「到底该用哪个?」我的回答是:看场景。
- 批处理为主,多引擎查询:Iceberg + Spark。Iceberg 的表格式规范让多引擎协作变得简单。
- 实时入湖,CDC 场景:Hudi + Flink。Hudi 的 Upsert 和增量查询能力是独门绝技。
- Databricks 生态:Delta Lake。跟 Spark 无缝集成,开箱即用。
- 流批一体:Flink + Iceberg。Flink 写 Iceberg 表,Spark 读,这是目前比较流行的组合。
注意:不要试图在一个项目里同时用三个存储格式。我见过有人 Iceberg、Hudi、Delta Lake 全用上,结果运维复杂度翻了好几倍。选一个,用透它,比什么都强。
好了,核心技术栈就聊到这儿。记住:工具是死的,场景是活的。理解每个组件的设计初衷,比背 API 重要得多。