第二章 核心概念与原理:增量计算的内功心法

说实话,很多同学一上来就问我:“老师,增量计算到底是个啥?跟批处理有啥区别?”

我习惯用一个比喻来解释:批处理就像每个月末做一次大扫除,把所有东西翻出来重新整理一遍;而增量计算呢,就像你每天随手把用完的东西放回原位,只处理变化的那一小块。前者稳定但笨重,后者轻巧但需要精细的“手眼协调”。

这一章,我们就来拆解增量计算的四个核心支柱:核心思想、数据变更捕获(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) 日志/触发器/时间戳 增量计算模型 流式/微批/增量批 状态管理与容错 Checkpoint/RocksDB 可追溯变更记录 可合并计算结果 基于日志 基于触发器 基于时间戳 流式处理 微批处理 增量批处理 内存状态 RocksDB 外部存储 选型时,先确定业务对延迟和状态大小的要求,再反推引擎和模型

嗯,这张图基本把增量计算的骨架搭起来了。从中心向外看,四个模块缺一不可。CDC 负责“看”,计算模型负责“算”,状态管理负责“记”,而核心思想则是贯穿始终的“魂”。