Spark 与 Flink:两种计算引擎的设计选择
上一篇解决了 Spark 的性能诊断方法。这一篇进入设计对比——Spark 和 Flink 两种计算引擎的架构选择。
这不是一个"谁更好"的问题。Spark 和 Flink 从不同的起点出发,做了不同的设计取舍,各自在擅长的场景中有结构性优势。Spark 从批处理出发,用 micro-batch 扩展到流处理;Flink 从流处理出发,把批看作有界流。两条路线在功能上逐步趋同,但底层架构的差异导致了性能特性和适用场景的持久分化。
本文只抓一个问题:两个引擎在执行模型、状态管理、容错机制和延迟特性上的具体差异及其设计原因。
执行模型对比
Spark 的执行模型是 Stage-based DAG:把计算图按 Shuffle 边界切成 Stage,每个 Stage 包含一组可以 pipeline 执行的窄依赖变换。Stage 之间是全局同步点——上游 Stage 的所有 Task 必须全部完成后,下游 Stage 才能启动。
1 | |
Flink 的执行模型是 Operator Chain + 持续流水线:算子之间通过 Network Buffer 异步传递数据,不需要等待上游全部完成。可以 chain 在一起的算子(窄依赖)融合到一个 Task 中执行,需要数据重分布的地方通过 Network Buffer 流式传输。
1 | |
核心差异:Spark 的 Stage 边界是同步的(barrier),Flink 的数据传输是异步的(pipelined)。这个差异直接决定了延迟特性——Spark 的延迟下限是一个 batch 的执行时间,Flink 可以做到逐条处理的毫秒级延迟。
批流统一的两种路线
1 | |
两条路线的实际效果:在纯批处理场景(ETL、数据仓库查询),Spark SQL 凭借 Catalyst+Tungsten+AQE 的成熟优化链通常性能更好。在低延迟流处理场景(实时告警、在线特征计算),Flink 凭借原生流式引擎有结构性优势。在批流混合场景(Lambda/Kappa 架构),两者都能胜任,选择取决于团队技术栈和延迟要求。
状态管理对比
1 | |
Flink 的 RocksDB State Backend 可以管理 TB 级的状态而不受 JVM 堆内存限制——数据存在本地 SSD 上,通过 LSM-Tree 结构高效读写。Spark 的 State Store 把全部状态放在 HashMap 中,状态大小受限于 Executor 内存。
对于状态量很大的流处理作业(如大窗口聚合、Session 窗口、复杂的 CEP 模式匹配),Flink 的状态管理有明显优势。
容错机制对比
1 | |
Chandy-Lamport 算法的关键特性:checkpoint barrier 在数据流中传播,每个算子在收到 barrier 时保存自己的状态快照。整个集群的 checkpoint 是异步完成的,不需要停止数据处理。
Spark 的 micro-batch checkpoint 发生在 batch 之间——每个 batch 处理完毕后写入 checkpoint。这意味着 checkpoint 频率等于 batch 频率。Flink 的 checkpoint 频率独立于数据处理——可以配置为每 10 秒做一次 checkpoint,即使数据是逐条处理的。
SQL 引擎对比
1 | |
Spark 的 Whole-Stage CodeGen 把一个 Stage 内的算子链编译成紧凑的 Java 方法,消除虚函数调用开销。Flink 在批处理模式下使用向量化执行(一次处理一批行而非一行),在流处理模式下逐条处理。
在 TPC-DS 等批处理基准测试中,Spark SQL 通常比 Flink SQL 快。在流式 SQL 场景(如持续聚合、时态 Join),Flink SQL 功能更完善。
适用场景总结
1 | |
实验:同一逻辑的批处理对比
1 | |
两者的逻辑完全相同,但底层执行路径不同:Spark 经过 Catalyst 四阶段优化 + Tungsten CodeGen,Flink 经过 Calcite 优化 + 向量化/逐条执行。
模式提炼
Spark 和 Flink 的分化体现了系统设计中的原点选择效应:从批出发做流,和从流出发做批,虽然功能上可以趋同,但底层架构的基因决定了各自的性能曲线。
这个效应在其他领域同样存在。PostgreSQL 从 OLTP 出发扩展分析能力(加并行查询、分区表),ClickHouse 从 OLAP 出发支持有限的事务。两者在功能上趋同,但性能特性由各自的存储引擎基因决定。iOS 从移动端出发做桌面化(iPadOS),macOS 从桌面出发做移动化(Apple Silicon Mac),两条路线在中间地带交汇但各保留了出发点的优势。
选择引擎的务实建议:不要追求"一个引擎解决所有问题"。评估当前和未来 1-2 年的核心场景,选择在该场景有结构性优势的引擎,然后在边缘场景做适度妥协。
工程迁移表
| 维度 | Spark | Flink |
|---|---|---|
| 执行模型 | Stage-based DAG(同步 barrier) | Operator Chain(异步 pipelined) |
| 批处理 | 原生(RDD / DataFrame / SQL) | 有界流(DataStream / Table API) |
| 流处理 | Structured Streaming(micro-batch) | 原生流(DataStream / Flink SQL) |
| 状态管理 | HDFSBackedStateStore(内存 HashMap) | RocksDB / HashMapStateBackend |
| 容错 | Lineage 重算 + checkpoint | Chandy-Lamport 分布式快照 |
| SQL 优化器 | Catalyst(自研) | Calcite(行业标准) |
| 代码生成 | Whole-Stage CodeGen | 无(向量化执行) |
| 运行时优化 | AQE(3.0+) | 无等价机制 |
| 延迟下限 | 百毫秒级(micro-batch) | 毫秒级(逐条) |
| 状态大小上限 | 受 Executor 堆内存限制 | 不受限(RocksDB on SSD) |
| 语言 | Scala / Java / Python / R / SQL | Java / Scala / Python / SQL |
常见误解
误解一:“Flink 比 Spark 快”。在流处理延迟上 Flink 确实更低,但在批处理吞吐上 Spark SQL 通常更快。"快"取决于场景。
误解二:“Spark 不能做实时处理”。Spark 的 Continuous Processing 模式可以做到毫秒级延迟,但功能受限(不支持聚合)。micro-batch 模式的延迟在数百毫秒到几秒之间,对很多"近实时"场景已经足够。
误解三:“选了 Spark 就不能用 Flink(或反之)”。很多组织同时使用两个引擎——Spark 做批处理和数据湖管理,Flink 做低延迟流处理。两者可以通过 Kafka、Hive Metastore、数据湖格式(Delta Lake、Iceberg)共享数据。
练习
-
用 Spark Structured Streaming 和 Flink DataStream 分别实现一个从 Kafka 读取数据做 wordcount 的流处理作业。对比两者的首条结果输出延迟和稳态吞吐。
-
构造一个需要大状态的流处理场景(如 1 小时窗口、100 万个 key 的聚合)。分别在 Spark(executor-memory=4g)和 Flink(RocksDB State Backend)上运行,对比状态管理的稳定性。
-
对同一份 Parquet 数据集执行 TPC-DS Q1 查询,分别用 Spark SQL 和 Flink SQL。对比执行时间和 explain 输出的物理计划差异。
系列导航
| 序号 | 主题 | 状态 |
|---|---|---|
| 00 | 导读:为什么 Spark 的核心是一张 DAG | 已完成 |
| 01 | RDD:弹性分布式数据集的五大属性 | 已完成 |
| 02 | 宽依赖与窄依赖:Stage 是怎样划分出来的 | 已完成 |
| 03 | DAG Scheduler 与 Task Scheduler:从逻辑计划到物理执行 | 已完成 |
| 04 | Shuffle 机制:数据跨分区交换的代价与优化 | 已完成 |
| 05 | 内存管理与 Tungsten:堆外内存、序列化与代码生成 | 已完成 |
| 06 | 存储体系:Block Manager、广播变量与累加器 | 已完成 |
| 07 | 容错机制:Lineage、Checkpoint 与推测执行 | 已完成 |
| 08 | Spark SQL 与 Catalyst:从 SQL 文本到物理算子树 | 已完成 |
| 09 | DataFrame 与 Dataset:类型安全与性能的折中 | 已完成 |
| 10 | Adaptive Query Execution:运行时改写执行计划 | 已完成 |
| 11 | 数据源 API 与谓词下推:让存储层帮忙过滤 | 已完成 |
| 12 | Structured Streaming:把流当成无界表 | 已完成 |
| 13 | 水位线与状态管理:流处理的时间语义 | 已完成 |
| 14 | Spark on YARN / K8s:资源管理与部署模式 | 已完成 |
| 15 | 动态资源分配与作业调度:多租户集群的资源博弈 | 已完成 |
| 16 | 性能诊断:Spark UI、Event Log 与调优思路 | 已完成 |
| 17 | Spark 与 Flink:两种计算引擎的设计选择 | 本篇 |
参考资料
- Matei Zaharia et al. Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing. NSDI 2012.
- Paris Carbone et al. Apache Flink: Stream and Batch Processing in a Single Engine. IEEE Data Engineering Bulletin, 2015.
- Tyler Akidau et al. The Dataflow Model. VLDB 2015.
- Apache Spark 官方文档:https://spark.apache.org/docs/latest/
- Apache Flink 官方文档:https://flink.apache.org/docs/
- Databricks Blog: https://www.databricks.com/blog
