第二章 核心概念与原理:增量计算的内功心法
说实话,很多同学一上来就问我:“老师,增量计算到底是个啥?跟批处理有啥区别?”
我习惯用一个比喻来解释:批处理就像每个月末做一次大扫除,把所有东西翻出来重新整理一遍;而增量计算呢,就像你每天随手把用完的东西放回原位,只处理变化的那一小块。前者稳定但笨重,后者轻巧但需要精细的“手眼协调”。
这一章,我们就来拆解增量计算的四个核心支柱:核心思想、数据变更捕获(CDC)、增量计算模型、状态管理与容错。搞懂这些,你选型时心里就有底了。
2.1 增量计算的核心思想:只算变化的部分
增量计算的核心,说白了就一句话:不重复计算已经算过的数据,只处理新增或变更的数据。
你想想看,传统批处理每次跑任务,都要把全量数据读一遍、算一遍。如果数据量是 100TB,每天只变了 1GB,那 99.9% 的计算都是浪费的。我在项目中遇到过一家电商公司,他们的离线报表每天跑 6 小时,后来改成增量模式,半小时就搞定了。
增量计算依赖两个前提:
- 可追溯的变更记录:系统必须知道“哪些数据变了”以及“怎么变的”。
- 可合并的计算结果:新结果必须能和老结果合并,而不是覆盖重算。
核心公式:Result(t) = Result(t-1) + Δ(Change)
其中 Δ 就是增量计算引擎要高效处理的“变化量”。
2.2 数据变更捕获(CDC):增量计算的“眼睛”
没有 CDC,增量计算就是瞎子。CDC 负责感知数据源的变化,并把这些变化以事件的形式暴露出来。
常见的 CDC 方式有四种,我整理了一张表,方便你对比:
| 方式 | 原理 | 延迟 | 对源库影响 | 典型工具 |
|---|---|---|---|---|
| 基于日志(Log-based) | 解析数据库事务日志(如 MySQL binlog) | 毫秒级 | 极小 | Debezium, Canal |
| 基于触发器(Trigger-based) | 在表上创建触发器,记录变更到审计表 | 秒级 | 较大(影响写性能) | 自定义脚本 |
| 基于时间戳(Timestamp-based) | 查询表中“最后修改时间”大于上次拉取时间的记录 | 分钟级 | 中等(需要索引) | SQOOP, 自定义 JDBC |
| 基于快照对比(Snapshot-based) | 定期对源表做全量快照,与上次快照做 diff | 小时级 | 大(全表扫描) | Spark, Flink 批模式 |
我的建议:生产环境首选基于日志的 CDC。我曾经用 Debezium + Kafka 做过一个实时数仓项目,binlog 解析延迟控制在 100ms 以内,而且对业务库几乎零侵入。触发器方式慎用,它会让你的数据库写操作变慢 30% 以上。
2.3 增量计算模型:三种主流范式
有了 CDC 提供的“变化”,接下来就要用计算模型来处理它。我归纳了三种主流模型,你可以根据业务场景对号入座。
2.3.1 流式处理模型(Stream Processing)
每条数据变更记录到达后立即处理。适合对延迟要求极高的场景,比如风控、实时大屏。
- 代表引擎:Apache Flink, Kafka Streams
- 优点:毫秒级延迟,天然支持 Exactly-Once
- 缺点:状态管理复杂,处理乱序数据需要 watermark
2.3.2 微批处理模型(Micro-Batch)
将短时间内的变更攒成一个小批次,统一处理。这是流和批的折中方案。
- 代表引擎:Spark Streaming(基于 DStream)
- 优点:复用批处理生态,容错简单(重算批次即可)
- 缺点:延迟受批次间隔限制(通常秒级),不适合毫秒级场景
2.3.3 增量批处理模型(Incremental Batch)
每次只处理新增的分区或文件,然后与历史结果合并。适合离线数仓的增量更新。
- 代表引擎:Apache Hive(增量分区)、Spark(增量覆盖)
- 优点:吞吐量高,适合大规模离线计算
- 缺点:延迟分钟级,需要依赖分区或文件级别的变更感知
避坑指南:我曾经在一个实时推荐项目中,盲目选择了微批处理,结果业务方要求 500ms 内看到用户行为反馈,微批的 2 秒间隔根本扛不住。后来换成了 Flink 纯流式,才把延迟压到 200ms。选模型前,一定先问清楚业务对延迟的真实容忍度。
2.4 状态管理与容错:增量计算的“记忆”与“恢复”
增量计算是有状态的。比如你要算“过去 1 小时的 UV”,引擎必须记住每个用户是否已经算过。如果引擎挂了,状态丢了,重启后就会算错。
状态管理是增量计算引擎最核心的能力之一,我把它拆成三个维度:
2.4.1 状态的存储方式
| 方式 | 描述 | 适用场景 |
|---|---|---|
| 内存状态 | 状态存储在 JVM 堆或堆外内存 | 状态小(<10GB),追求极致性能 |
| 嵌入式 KV 存储 | 如 RocksDB,状态持久化到本地磁盘 | 状态大(10GB~TB 级),需要容错 |
| 外部存储 | 状态存储在 Redis、HBase 等外部系统 | 需要跨作业共享状态,或状态极大 |
2.4.2 容错机制:Checkpoint 与 Savepoint
容错的核心是定期给状态拍“快照”。
- Checkpoint:自动触发的轻量级快照,用于故障恢复。Flink 的 Checkpoint 基于 Chandy-Lamport 分布式快照算法,一致性很高。
- Savepoint:手动触发的重量级快照,用于版本升级、作业迁移。我习惯在每次上线新逻辑前,手动打一个 Savepoint,万一出问题可以秒级回滚。
关键配置:Checkpoint 间隔不宜过短(否则影响性能),也不宜过长(否则恢复时间久)。我一般建议设置为 1-3 分钟,同时开启 exactly-once 语义。
2.4.3 状态后端的选择
Flink 的状态后端是典型代表,我直接给出我的选型建议:
- MemoryStateBackend:仅用于开发测试,生产环境别用。
- FsStateBackend:状态存本地,Checkpoint 写 HDFS/S3。适合状态中等(<50GB)的场景。
- RocksDBStateBackend:状态存 RocksDB,支持增量 Checkpoint。适合状态超大(>50GB)的场景。我在一个百亿级事件量的项目中,就是用 RocksDB 扛住了 200GB 的状态。
2.5 知识体系总览:一张图看懂增量计算
下面这张 SVG 图,把本章的核心概念串了起来。你可以把它当作增量计算引擎的“思维导图”。
嗯,这张图基本把增量计算的骨架搭起来了。从中心向外看,四个模块缺一不可。CDC 负责“看”,计算模型负责“算”,状态管理负责“记”,而核心思想则是贯穿始终的“魂”。