增量ETL架构设计:从Lambda到流批一体
聊到增量ETL架构,我脑子里第一个蹦出来的词就是「纠结」。为什么?因为数据团队永远在实时性和准确性之间反复横跳。早些年我们只能二选一,要么T+1跑批,要么搞复杂的Lambda。现在好了,选择多了,但坑也多了。这一章我把三种主流架构掰开揉碎讲清楚,顺便聊聊我踩过的那些坑。
Lambda架构详解
Lambda架构,说白了就是「两条腿走路」。一条批处理腿,保证数据最终准确;一条流处理腿,保证数据尽快可见。两套代码,两套管道,最后在服务层合并。
核心思想:批处理层(Batch Layer)+ 速度层(Speed Layer)+ 服务层(Serving Layer)
我记得2017年接手过一个电商大屏项目。业务方要求实时GMV,但财务又要求数据必须和T+1报表一致。当时没别的办法,只能上Lambda。批处理层用Spark跑全量重算,速度层用Flink消费Kafka做增量聚合,最后在Redis里合并结果。
架构长这样:
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%,爽得很。
流批一体的好处很明显:
- 代码复用——一套逻辑,两种模式
- 运维简化——不用维护两套管道
- 口径一致——同一个算子,结果自然一样
但也不是没坑。流批一体的状态管理比纯流处理更复杂。批处理模式下状态是瞬时的,流处理模式下状态是持久的。切换模式时,状态兼容性是个大问题。
避坑指南:我曾经在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简洁但有状态风险,流批一体是趋势但还不够成熟。我的建议是:从最简单的模式开始,等业务逼着你升级了,再考虑更复杂的架构。别为了炫技而炫技,数据工程的核心是「把数据稳定、准确地交付出去」。