Spark 与 Flink:两种计算引擎的设计选择
上一篇解决了 Spark 的性能诊断方法。这一篇进入设计对比——Spark 和 Flink 两种计算引擎的架构选择。
这不是一个"谁更好"的问题。Spark 和 Flink 从不同的起点出发,做了不同的设计取舍。Spark 以批处理引擎起家,Structured Streaming 默认使用 micro-batch,当前版本也提供受限定的 Real-Time Mode;Flink 以持续流处理为基础,同时为有界输入提供专门的批执行模式。两条路线在 API 层逐步趋同,但执行方式、状态后端和运行时能力仍然需要按具体版本与负载评估。
本文只抓一个问题:两个引擎在执行模型、状态管理、容错机制和延迟特性上的具体差异及其设计原因。
执行模型对比
Spark 的执行模型是 Stage-based DAG:把计算图按 Shuffle 边界切成 Stage,每个 Stage 包含一组可以 pipeline 执行的窄依赖变换。Stage 之间是全局同步点——上游 Stage 的所有 Task 必须全部完成后,下游 Stage 才能启动。
1 | |
Flink 的执行模型是 Operator Chain + 持续流水线:算子之间通过 Network Buffer 异步传递数据,不需要等待上游全部完成。可以 chain 在一起的算子(窄依赖)融合到一个 Task 中执行,需要数据重分布的地方通过 Network Buffer 流式传输。
1 | |
这种对比适用于 Spark 常规批处理与 Structured Streaming 的默认 micro-batch 模式。Spark 批任务通常在 Shuffle 边界上形成 Stage,Flink 流任务则可以在算子之间持续流水传输。实际延迟还受触发器、状态算子、数据源、Sink 和背压影响;Spark Real-Time Mode 在其支持的无状态算子与连接器范围内,也把延迟目标降到了亚秒级甚至更低。
批流统一的两种路线
1 | |
批处理场景可以比较 Catalyst、AQE、Flink 批运行时与数据源适配;低延迟流处理则要连同状态算子、一致性、连接器支持和延迟目标一起评估。不同版本、查询和存储格式的结果可能反转,因此不用单个基准测试给引擎设定固定排名。
状态管理对比
1 | |
这里的关键不是把 Spark 等同于堆内 HashMap、把 Flink 等同于 RocksDB,而是核对状态 provider/backend 的选型、checkpoint 存储、TTL、分区数以及恢复带宽。大状态作业应使用接近生产规模的数据测量,不能只从默认配置推断引擎上限。
容错机制对比
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 把可融合的算子编译成紧凑的 Java 方法。Flink Table/SQL 同样有代码生成,并且为有界和无界输入配置不同的优化规则与运行时算子。两者的实际差异应通过目标版本的 EXPLAIN、稳态负载和故障恢复实验确认。
适用场景总结
1 | |
实验:同一逻辑的批处理对比
1 | |
两者表达的聚合逻辑接近,但底层执行路径不同:Spark 经过 Catalyst 优化与物理计划选择,Flink 经过 Calcite 与批运行时算子生成计划。不应把两者简化成"一个代码生成、一个只做向量化"。
模式提炼
Spark 以批处理起家,Flink 以流处理起家。两者的 API 功能可以趋同,但执行模型、状态机制和运行时优化仍会形成不同的性能曲线。
这个效应在其他领域同样存在。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;另有受限定的 Real-Time Mode) | DataStream / Flink SQL |
| 状态管理 | 可配置 State Store provider(内置 HDFSBacked / RocksDB) | HashMap / EmbeddedRocksDB / ForSt |
| 容错 | Lineage 重算 + checkpoint | Chandy-Lamport 分布式快照 |
| SQL 优化器 | Catalyst(自研) | Calcite(行业标准) |
| 代码生成 | Whole-Stage CodeGen | Table/SQL 生成算子代码 |
| 运行时优化 | AQE | 按批/流模式优化,能力不宜与 AQE 一一对应 |
| 延迟目标 | micro-batch 与受限定的 Real-Time Mode | 持续流水执行 |
| 状态规模上限 | 取决于 provider、分区、内存与磁盘 | 取决于 backend、快照存储与恢复带宽 |
| 语言 | Scala / Java / Python / R / SQL | Java / Scala / Python / SQL |
常见误解
误解一:“Flink 比 Spark 快"或"Spark 批处理一定更快”。延迟、吞吐、成本和恢复时间是不同指标,结论还会随版本、算子、数据格式和资源配置改变。
误解二:“Spark 不能做低延迟处理”。Structured Streaming 默认的 micro-batch 适合秒级或分钟级管道;当前 Real-Time Mode 面向亚秒级业务,但只支持定义过的 Source、无状态算子与 Sink 集合,不能把它当作默认模式的无条件替代。
误解三:“选了 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:两种计算引擎的设计选择 | 本篇 |
Flink 部署、角色、Checkpoint 与故障恢复图示见: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 Spark Structured Streaming:https://spark.apache.org/streaming/
- Apache Spark State Store:https://spark.apache.org/docs/latest/streaming/structured-streaming-state-data-source.html
- Apache Flink State Backends:https://nightlies.apache.org/flink/flink-docs-stable/docs/ops/state/state_backends/
- Apache Flink Table/DataStream 执行模式:https://nightlies.apache.org/flink/flink-docs-stable/docs/dev/table/data_stream_api/
- Databricks Blog: https://www.databricks.com/blog
