技术选型对比:Flink vs Spark Streaming、Kafka vs Pulsar、Hudi vs Iceberg vs Delta Lake
做流批一体特征计算,第一步不是写代码,而是选型。选错了,后面三个月都在填坑。我经历过几次选型失误,最惨的一次是团队拍脑袋选了某个组件,结果上线后数据延迟从秒级变成分钟级,运维同学差点跟我翻脸。
这一章,我把三个核心维度的选型经验拆开来讲。你想想看,流计算引擎、消息队列、存储层,这三层只要有一层选错,整个架构就歪了。
流计算引擎:Flink vs Spark Streaming
先说结论:做特征计算,我首选 Flink。这不是偏见,是实战摔出来的经验。
| 对比维度 | Flink | Spark Streaming |
|---|---|---|
| 事件时间语义 | 原生支持,毫秒级精度 | 需要额外配置,微批次有延迟 |
| 状态管理 | Keyed State + RocksDB,支持大状态 | 基于 RDD 的 checkpoint,状态规模受限 |
| Exactly-Once | 端到端内置 | 需要两阶段提交,配置复杂 |
| 批流一体 | 同一 API 处理批和流 | 批流 API 不同,迁移成本高 |
| 延迟 | 纯流模式,毫秒级 | 微批次,秒级起步 |
| 社区活跃度 | 极高,阿里主导 | 高,但流方向投入减少 |
核心观点:特征计算对事件时间和状态一致性要求极高。Flink 的事件时间语义是原生的,而 Spark Streaming 的微批次架构天然存在窗口边界模糊的问题。我在做用户实时画像时,Spark Streaming 的窗口漂移问题让我排查了整整两天,换成 Flink 后一次搞定。
为什么 Spark Streaming 在特征计算场景下吃亏?说白了,特征计算需要精确的「事件发生时间」,而不是「数据到达时间」。Spark Streaming 的微批次把数据攒一批再处理,时间戳已经被污染了。你想想看,一个用户点击发生在 10:00:01,但批次在 10:00:05 才处理,这个 4 秒的偏差对实时特征来说就是灾难。
Flink 怎么解决的?它用 Watermark 机制来处理乱序数据。我记得第一次用 Flink 做点击流特征时,设置了一个 5 秒的 allowed lateness,结果数据准确率从 92% 直接跳到 99.7%。
个人建议:如果你的特征计算对延迟要求 < 1 秒,或者需要复杂的事件时间窗口,直接选 Flink。如果团队 Spark 技术栈很成熟,且延迟容忍度在 5 秒以上,Spark Streaming 也能用,但要做好状态管理的心理准备。
消息队列:Kafka vs Pulsar
消息队列是特征计算的「血管」。数据进不来,后面全是白搭。
| 对比维度 | Kafka | Pulsar |
|---|---|---|
| 存储架构 | 分区日志,依赖磁盘顺序读写 | 存储计算分离,BookKeeper + Broker |
| 消息保留 | 基于时间/大小,过期删除 | 支持分层存储,可保留海量历史 |
| 消费模型 | 分区独占/共享 | 支持独占、共享、灾备多种模式 |
| 运维复杂度 | 中等,依赖 ZooKeeper | 较高,组件多 |
| 吞吐量 | 极高,百万级/秒 | 高,但单机略逊于 Kafka |
| 延迟 | 毫秒级 | 毫秒级,略高于 Kafka |
我在项目中遇到过一个问题:Kafka 的分区数一旦超过 1000,rebalance 就会变得非常慢。有一次我们做特征回放,需要保留 7 天的消息,Kafka 的磁盘直接被打满。后来换成 Pulsar 的分层存储,冷数据自动迁移到 HDFS,问题就解决了。
避坑指南:我曾经因为 Kafka 的「消息回溯」能力不足,导致特征回放任务失败。Kafka 虽然支持 offset 重置,但大规模回溯时性能下降明显。Pulsar 的 BookKeeper 存储天然支持高效回溯,如果你需要频繁做特征回放,Pulsar 是更好的选择。
选型建议很简单:
- 团队小、运维能力弱:选 Kafka。生态成熟,文档多,出问题能搜到答案。
- 需要多租户、海量消息保留、频繁回溯:选 Pulsar。虽然运维重,但功能确实强。
- 特征回放场景多:Pulsar 的分层存储是杀手锏。
存储层:Hudi vs Iceberg vs Delta Lake
存储层是流批一体最难选的部分。这三个项目都在解决同一个问题:让数据湖支持 ACID 和增量更新。但实现方式差异很大。
| 对比维度 | Apache Hudi | Apache Iceberg | Delta Lake |
|---|---|---|---|
| 核心设计 | 增量处理优先 | 表格式标准化 | Spark 生态深度绑定 |
| 写入模式 | Copy-on-Write / Merge-on-Read | Copy-on-Write / Merge-on-Read | Copy-on-Write / Merge-on-Read |
| ACID 事务 | 基于时间线 | 基于快照隔离 | 基于事务日志 |
| Flink 集成 | 成熟,支持流写流读 | 成熟,支持流写批读 | 较弱,主要面向 Spark |
| 增量查询 | 原生支持,性能好 | 需要额外配置 | 支持,但不如 Hudi 灵活 |
| Schema 演进 | 支持 | 支持,更灵活 | 支持 |
我的选择:如果 Flink 是主力引擎,选 Hudi。如果团队 Spark 和 Flink 混用,选 Iceberg。如果全栈 Spark,Delta Lake 也不错,但要注意它不是 Apache 项目。
为什么我推荐 Hudi 做特征存储?因为特征计算的核心场景是「增量更新」和「增量读取」。Hudi 的增量查询能力是三个项目里最强的。我记得有一次做实时特征拼接,需要每 5 分钟读取过去 1 小时的新增特征,Hudi 的增量查询直接返回变更的数据文件,性能比全表扫描快了 10 倍。
Iceberg 的优势在于表格式的标准化。它把表格式定义得非常干净,理论上任何引擎都能接入。如果你有多引擎需求(Flink、Spark、Trino、Presto),Iceberg 是更好的选择。
Delta Lake 的问题在于它和 Spark 绑定太深。虽然现在也支持 Flink,但集成度远不如 Hudi 和 Iceberg。如果你不是全栈 Spark,建议慎重。
避坑指南:我曾经在 Hudi 的 Merge-on-Read 模式下踩过坑——小文件太多导致查询性能雪崩。后来设置了合理的清理策略和 clustering 频率,问题才解决。选 Hudi 的话,一定要提前规划好文件大小和清理策略。
整体选型框架
下面这张图是我做选型时用的决策框架,你可以直接拿去用。
选型没有银弹。我见过用 Flink + Pulsar + Iceberg 跑得飞快的团队,也见过用 Spark + Kafka + Delta Lake 做得不错的。关键是你得清楚自己的场景:
- 实时特征拼接:Flink + Kafka + Hudi
- 多引擎数据湖:Flink + Pulsar + Iceberg
- 全栈 Spark 团队:Spark + Kafka + Delta Lake
- 高吞吐、低延迟:Flink + Kafka + Hudi(Merge-on-Read 模式)
重要提醒:选型不是一次性的。我建议每半年重新评估一次技术栈。我见过一个团队三年前选了 Delta Lake,结果后来需要接入 Flink,发现集成成本极高,最后不得不迁移到 Iceberg。选型时留一点「迁移余地」,别把路走死。