Flink 在流批一体架构中的角色与最佳实践
从 Lambda 到 Flink 流批一体的演进路径,结合字节跳动推荐场景展示计算同源加存储同源的工程价值
从 Lambda 到流批一体:架构演进的三段路
大数据架构在过去十年经历了三个标志性阶段,每个阶段都是对上一阶段痛点的回应。
Lambda 架构:两套管线的痛苦。Lambda 架构由 Nathan Marz 在 2011 年提出,核心思想是"批处理层保证准确性,速度层保证实时性"。速度层用 Storm 或 Flink 处理实时数据,批处理层用 MapReduce 或 Spark 每天全量重算。两条管线分别维护,结果在服务层合并。
Lambda 架构的致命问题在于维护成本:同一份业务逻辑需要在两套技术栈上各实现一遍。批处理代码用 Spark Scala 写,实时代码用 Flink Java 写,两者的语义对齐、结果一致性验证、bug 修复都是双倍工作量。字节跳动在 2018 年前就深受其害——推荐系统的实时特征和离线特征分别由两套管线产出,经常出现特征不一致导致的模型效果波动。
Kappa 架构:理想化的全流式。Jay Kreps 在 2014 年提出 Kappa 架构,核心主张是"一切皆流"——取消批处理层,所有计算都通过流处理引擎完成,需要重算历史时就回放消息队列中的数据。Kappa 架构在理念上解决了双管线问题,但在实践中面临两个障碍:一是回放海量历史数据的成本极高(Kafka 保留数月的数据需要 TB 级存储,回放时间可能是原始处理时间的数十倍);二是批处理在某些场景(如复杂多维聚合、机器学习特征工程)中的效率远高于逐条流处理。
Flink 流批一体:第三条路。Flink 从设计之初就将批处理视为流处理的特例——批处理就是一个有界的流(Bounded Stream)。这种统一抽象使得同一套 Flink 作业可以同时处理实时数据(Unbounded Stream)和历史数据(Bounded Stream),代码只需编写一次。2020 年 Flink 1.12 正式引入批处理模式,2023 年后流批一体在生产环境中趋于成熟。
计算同源 + 存储同源:工程价值的两面
流批一体的真正价值不仅在于"用一个引擎",而在于计算层和存储层的同时统一。
计算同源的含义是:实时计算和离线计算使用相同的代码逻辑。在推荐系统场景中,这意味着特征提取代码只需要维护一份。当业务逻辑变更时(如调整用户兴趣标签的计算规则),修改一处代码即可同时影响实时特征和离线特征,从根源上消除了 Lambda 架构中的特征不一致问题。
字节跳动在其推荐系统中验证了这个价值:将 Flink 流批一体作业部署在 Iceberg 表上后,实时特征和离线特征的一致性问题从每月发生 3–5 次降到了零。更关键的是,特征开发周期从"实时管线 2 天 + 离线管线 3 天"缩短到"统一管线 2 天",开发效率提升约 40%。
存储同源的含义是:实时数据和历史数据存储在同一份数据上,而不是实时数据写入 Kafka/OLAP、离线数据写入 Hive/数据湖的双写模式。Apache Iceberg 和 Apache Paimon 是目前最常用的存储同源方案——它们都支持流式写入和批量读取,使得 Flink 作业可以将实时结果直接写入湖表,而离线分析引擎(如 Spark、Trino)可以直接查询同一份数据。
存储同源消除了"实时表"和"离线表"之间的数据同步问题。在传统的双写架构中,Kafka 中的实时数据和 Hive 中的离线数据经常存在时间窗口差异,BI 报表在"实时模式"和"离线模式"下看到的数字不一致是常态。流批一体 + 湖仓存储从架构上消除了这个问题。
实时开发 StreamWorks
覆盖采集、开发、运维全链路的云原生一站式实时数据开发平台
PoC 验证:在正式迁移前跑通关键路径
流批一体架构的 PoC 不是"跑一个 Hello World",而是要验证五个关键能力:
能力一:同一作业的流批切换。编写一个 Flink 作业,先以 Batch 模式跑通历史数据回填,然后切换为 Streaming 模式处理增量数据,验证两种模式的输出结果一致。
能力二:Checkpoint + 历史回放。验证 Flink 作业故障恢复后,能否从 Checkpoint 正确恢复状态,并继续处理未消费的数据。在流批一体场景中,还需要验证 Batch 模式下跑历史数据时 Checkpoint 的行为是否符合预期。
能力三:Schema 变更处理。在 PoC 期间模拟一次上游 Schema 变更(如新增字段),验证 Flink 作业和下游表格式(Iceberg/Paimon)能否正确处理——是忽略新字段、报错、还是自动扩展。
能力四:端到端延迟。在 Streaming 模式下,测量从数据写入源表到可在目标表中查询到的端到端延迟。这个延迟包括 Flink 的处理延迟 + 表格式的 Commit 延迟 + 查询引擎的可见性延迟。
能力五:资源消耗对比。对比 Batch 模式跑全量历史和 Streaming 模式处理同等数据量的资源消耗。如果 Batch 模式的资源消耗是纯 Spark 批处理的 2 倍以上,需要评估是否值得为了代码统一而接受这个成本。
高质量数据集
围绕采集、清洗、标注、评估、治理与合规管理,把多模态原始数据构建为面向 AI 与业务应用的高质量数据集。
渐进迁移路径:从增量场景到全量替换
不建议一步到位替换所有批处理管线。推荐的迁移路径:
第一阶段:选择低风险的增量场景。找一个现有的"实时 + 离线双写"场景——例如用户行为统计报表。这个场景的特点是业务逻辑简单、数据量可控、失败影响有限。用 Flink 流批一体作业替换原有的双管线,以 Iceberg 或 Paimon 作为统一存储层。
第二阶段:验证运维体系。在第一个场景跑稳后,建立流批一体作业的运维规范:如何监控作业状态、如何处理 Checkpoint 失败、如何做版本升级和灰度发布、如何处理数据回填。这些运维能力是后续大规模迁移的基础。
第三阶段:扩展到复杂场景。将流批一体扩展到更复杂的业务场景——推荐特征计算、实时数仓分层、数据质量校验。这些场景的挑战在于状态管理(Flink State 的大小控制和 TTL 策略)和端到端一致性保证(Exactly-Once 语义在 Sink 端的实现)。
第四阶段:下线旧管线。当流批一体管线覆盖了 80% 以上的实时 + 离线场景后,开始逐步下线原有的 Lambda 架构管线。下线的顺序是:先下线数据源相同的重复管线,再下线逻辑相似的冗余作业,最后处理剩余的遗留系统。
常见挑战与应对
状态膨胀。Flink 流批一体作业在 Streaming 模式下需要维护状态(如窗口聚合的中间结果),当状态量增长到 TB 级别时,Checkpoint 耗时和失败率都会上升。应对方案包括:使用 RocksDB 状态后端 + 增量 Checkpoint、设置合理的 State TTL、对大状态场景使用分桶策略将状态分散到多个 KeyGroup。
Batch 模式性能。Flink 的 Batch 模式在复杂 Shuffle 场景下的性能通常不如 Spark——因为 Flink 的 Batch 执行器是为容错设计的(细粒度恢复),而 Spark 的执行器是为吞吐设计的(粗粒度 Stage 重算)。对于纯离线的大规模 ETL 作业,如果性能是关键约束,可以保留 Spark 作为 Batch 模式的执行引擎,仅在流批一体的增量场景中使用 Flink。
团队技能转型。流批一体要求开发者同时理解流处理(事件时间、Watermark、状态管理)和批处理(分区裁剪、数据倾斜、资源调度)的概念。这对习惯了 Spark 批处理的团队是一个显著的学习曲线。建议投入 2–4 周的专项培训,重点覆盖 Flink 的时间语义、State管理和 Connector 使用。
在工程落地层面,StreamWorks + BatchWorks 等平台工具提供了基于 Flink 引擎的实时采集、开发与运维全链路能力,支撑 Flink 流批一体开发的全流程——从 CDC 数据接入、流作业开发调试到批处理回填和运维监控,降低了团队从零搭建流批一体基础设施的门槛。
流批一体不是一个非黑即白的决策——它是一个渐进的架构演进过程。核心目标不是"消灭批处理",而是"用一份代码、一份数据同时满足实时和离线的需求"。在这个过程中,选择正确的切入点、建立可靠的运维体系、控制迁移风险,比追求技术纯粹性更重要。


