增量ETL架构设计:从Lambda到流批一体

聊到增量ETL架构,我脑子里第一个蹦出来的词就是「纠结」。为什么?因为数据团队永远在实时性和准确性之间反复横跳。早些年我们只能二选一,要么T+1跑批,要么搞复杂的Lambda。现在好了,选择多了,但坑也多了。这一章我把三种主流架构掰开揉碎讲清楚,顺便聊聊我踩过的那些坑。

Lambda架构详解

Lambda架构,说白了就是「两条腿走路」。一条批处理腿,保证数据最终准确;一条流处理腿,保证数据尽快可见。两套代码,两套管道,最后在服务层合并。

核心思想:批处理层(Batch Layer)+ 速度层(Speed Layer)+ 服务层(Serving Layer)

我记得2017年接手过一个电商大屏项目。业务方要求实时GMV,但财务又要求数据必须和T+1报表一致。当时没别的办法,只能上Lambda。批处理层用Spark跑全量重算,速度层用Flink消费Kafka做增量聚合,最后在Redis里合并结果。

架构长这样:

Lambda 架构示意图 数据源(Kafka) 批处理层 Spark 全量重算 速度层 Flink 实时增量 服务层 合并查询 + 数据服务 ⚠ 注意: 批处理层和速度层需要维护两套代码逻辑,运维成本高。 数据口径不一致是常见问题,建议用同一套UDF库。

Lambda的好处是成熟、稳定,大厂验证过。但坏处也很明显——你得维护两套代码。我曾经见过一个团队,批处理和流处理的聚合逻辑差了0.5%,排查了整整两周,最后发现是窗口边界定义不一致。

避坑指南:我曾经在Lambda架构里犯过一个低级错误——批处理和流处理用了不同的时间戳解析库。结果同一批数据,两边算出来的「当天」差了8小时。后来我强制要求所有时间处理逻辑必须封装成共享UDF,两边调用同一份代码。

Kappa架构详解

Kappa架构就简单多了——只用一套流处理。所有数据都走实时管道,需要重算时,把Kafka的offset往前调,重新消费一遍历史数据就行。

说白了,Kappa就是「一条道走到黑」。你想想看,既然流处理已经能处理无限数据流了,为什么还要分两套?

核心思想:统一使用流处理引擎,历史数据通过「重放」机制重新计算。

我个人习惯在数据量不大(日均TB级以下)、实时性要求高的场景用Kappa。比如用户行为分析、实时推荐这类场景,Kappa的简洁性简直是救星。

但Kappa有个硬伤——状态管理。流处理的状态会随着时间膨胀,Checkpoint做不好,一旦故障恢复,那叫一个酸爽。我记得有一次Flink的状态后端用RocksDB,磁盘写爆了,恢复花了3个多小时。

对比维度 Lambda Kappa
代码维护 两套代码,成本高 一套代码,简洁
数据一致性 最终一致,需合并 强一致(单管道)
历史重算 批处理全量重算 Kafka重放
运维复杂度 高 中
适用场景 大厂、复杂业务 中小规模、实时优先

我的建议:如果你团队不大,或者业务对实时性要求没那么变态,别轻易上Lambda。Kappa够用就用Kappa。实在不行,后面还有流批一体这个选项。

流批一体架构

流批一体,这几年最火的方向。说白了就是「一套代码,两种运行模式」。批处理是流处理的特殊情况——把数据源当成有界流就行。

Flink和Spark都支持这个模式。Flink的DataStream API和Table API可以无缝切换批/流;Spark的Structured Streaming也是流批一体的思路。

我在2021年帮一个金融客户做风控系统,用的就是流批一体。白天跑实时规则,晚上用同一套代码跑T+1全量校验。代码复用率接近100%,爽得很。

流批一体架构示意图 统一计算逻辑 批模式(Batch) 有界流处理 流模式(Stream) 无界流处理 统一输出(OLAP)

流批一体的好处很明显:

  • 代码复用——一套逻辑,两种模式
  • 运维简化——不用维护两套管道
  • 口径一致——同一个算子,结果自然一样

但也不是没坑。流批一体的状态管理比纯流处理更复杂。批处理模式下状态是瞬时的,流处理模式下状态是持久的。切换模式时,状态兼容性是个大问题。

避坑指南:我曾经在Flink流批一体模式下,批处理用了ValueState,流处理用了MapState。结果模式切换时状态恢复失败。后来我统一用KeyedState,并且给状态设置了TTL,才解决这个问题。

增量ETL管道设计模式

聊完架构,咱们落地到具体管道设计。增量ETL管道,说白了就是「怎么把变化的数据高效地搬进OLAP」。我总结了三种常用模式:

模式一:CDC + 实时写入

通过Debezium或Canal监听数据库binlog,解析成变更事件,直接写入OLAP。适合数据量不大、实时性要求高的场景。

-- 伪代码示例:CDC管道
Source: MySQL Binlog (Debezium)
  → Transform: 解析为INSERT/UPDATE/DELETE事件
  → Sink: ClickHouse MergeTree表(ReplacingMergeTree去重)

模式二:微批次 + 窗口聚合

每隔N秒/分钟拉取增量数据,做一次微批次聚合。适合对实时性要求不那么变态的场景。

-- 伪代码示例:微批次管道
Source: Kafka Topic (增量数据)
  → Window: TumblingWindow(1分钟)
  → Aggregate: SUM/GROUP BY
  → Sink: Doris/StarRocks 明细表

模式三:Lambda + 合并查询

批处理层做全量重算,速度层做实时增量,服务层合并。适合对准确性要求极高的场景。

模式 延迟 准确性 运维成本 推荐场景
CDC + 实时 秒级 最终一致 低 实时大屏、监控
微批次 分钟级 较高 中 报表、BI分析
Lambda 秒级+小时级 高 高 金融、财务

我的经验:别一上来就搞Lambda。先问自己三个问题:数据延迟容忍度是多少?数据准确性要求多高?团队有多少人?如果延迟容忍度在分钟级,微批次模式性价比最高。我见过太多团队为了「实时」而实时,最后把自己搞崩了。

嗯,架构选型没有银弹。Lambda稳定但重,Kappa简洁但有状态风险,流批一体是趋势但还不够成熟。我的建议是:从最简单的模式开始,等业务逼着你升级了,再考虑更复杂的架构。别为了炫技而炫技,数据工程的核心是「把数据稳定、准确地交付出去」。