3、实时流处理框架:Apache Flink 核心概念、Flink CDC 原理、Kafka Streams 对比

聊到实时血缘捕获,绕不开的就是流处理框架。我这些年做数据治理,见过不少团队在选型上纠结。其实说白了,选哪个框架,取决于你的场景和团队技术栈。今天我就把 Flink 和 Kafka Streams 这两个主流方案掰开揉碎讲清楚。

3.1 Apache Flink 核心概念

Flink 是什么?它是一个真正的流处理引擎。注意我强调「真正」这个词。早期很多所谓的流处理框架,其实是微批处理——把数据攒成小批次再算。Flink 不一样,它来一条处理一条,延迟极低。

3.1.1 有界流与无界流

这是 Flink 最基础的概念:

  • 有界流(Bounded Stream):数据有起点和终点,比如跑一个离线任务处理昨天的日志。处理完就结束。
  • 无界流(Unbounded Stream):数据源源不断,比如 Kafka 里的实时交易流水。理论上永远不会停。

我在项目中遇到过有人把无界流当有界流处理,结果任务跑完就停了,后面的数据全丢了。嗯,这里要注意:实时血缘捕获一定是无界流场景。

3.1.2 时间语义

Flink 支持三种时间:

时间类型 说明 适用场景
事件时间(Event Time) 数据本身携带的时间戳 实时血缘,必须用这个
摄入时间(Ingestion Time) 数据进入 Flink 的时间 对时间精度要求不高的场景
处理时间(Processing Time) 算子本地处理的时间 性能测试、简单聚合

我个人习惯在血缘捕获中只用事件时间。为什么?因为数据可能延迟到达,如果用处理时间,血缘关系的时间线就乱了。你想想看,一条 SQL 执行了 5 秒,但它的血缘关系应该基于 SQL 提交的时间,而不是 Flink 处理到这条日志的时间。

3.1.3 Checkpoint 与状态后端

Flink 的容错机制靠的是 Checkpoint。它会定期保存算子状态的快照。一旦任务挂了,就从最近的 Checkpoint 恢复。

我曾经因为 Checkpoint 间隔设得太短(1 秒一次),导致 HDFS 被小文件撑爆。后来调整为 10 秒一次,配合增量 Checkpoint,问题就解决了。

状态后端有三种选择:

  • MemoryStateBackend:存在 JVM 堆内存,适合调试,生产别用。
  • FsStateBackend:存在文件系统,适合状态不大的场景。
  • RocksDBStateBackend:存在本地磁盘,支持超大状态。我推荐这个。

3.2 Flink CDC 原理

CDC(Change Data Capture)就是捕获数据库的变更数据。Flink CDC 是基于 Debezium 实现的,但比 Debezium 更「Flink 化」。

3.2.1 核心原理

Flink CDC 通过读取数据库的 binlog(MySQL)或 WAL(PostgreSQL),把数据库的 INSERT、UPDATE、DELETE 操作转换成流式数据。它的工作流程是这样的:

  1. 连接数据库:Flink CDC Source 连接到 MySQL,模拟成 Slave 节点。
  2. 读取 binlog:实时拉取 binlog 的变更事件。
  3. 解析成 RowData:把 binlog 事件解析成 Flink 内部的 RowData 结构。
  4. 输出到下游:RowData 进入 Flink 的 DataStream,后续可以做任意处理。

这里有个关键点:Flink CDC 支持全量+增量模式。第一次启动时,它会先做一次快照读取(全量),然后无缝切换到 binlog 监听(增量)。我在项目中用这个功能做数据迁移,零停机时间。

关键优势:Flink CDC 2.0+ 版本支持动态加表,不需要重启任务。这对实时血缘捕获来说太重要了——业务方随时可能新增一张表,你不能每次都停任务。

3.2.2 代码示例

下面是一个简单的 Flink CDC 任务,读取 MySQL 的变更数据:

// 创建 MySQL CDC Source
MySqlSource<String> mySqlSource = MySqlSource.<String>builder()
    .hostname("localhost")
    .port(3306)
    .databaseList("my_db")          // 监控的数据库
    .tableList("my_db.orders")      // 监控的表
    .username("cdc_user")
    .password("cdc_pass")
    .deserializer(new JsonDebeziumDeserializationSchema())
    .startupOptions(StartupOptions.initial())  // 全量+增量
    .build();

// 构建 Flink 任务
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(10000);  // 10秒一次 Checkpoint

DataStreamSource<String> cdcStream = env
    .fromSource(mySqlSource, WatermarkStrategy.noWatermarks(), "MySQL CDC Source");

// 处理变更数据
cdcStream.map(new MapFunction<String, String>() {
    @Override
    public String map(String value) throws Exception {
        // 解析 JSON,提取血缘信息
        JSONObject json = JSONObject.parseObject(value);
        String op = json.getString("op");  // c: insert, u: update, d: delete
        JSONObject after = json.getJSONObject("after");
        // ... 提取字段级血缘
        return value;
    }
}).print();

env.execute("Real-time Lineage Capture");

避坑指南:我曾经在生产环境遇到 CDC 任务 OOM,原因是 binlog 积压太多。后来加了限流和背压处理,才稳定下来。建议设置 debezium.max.queue.size 参数,默认是 8192,可以根据数据量调大。

3.3 Kafka Streams 对比

Kafka Streams 是另一个流处理方案。它不像 Flink 那样是一个独立的计算框架,而是嵌入在应用里的一个库。两者各有千秋,我列个表对比一下:

对比维度 Apache Flink Kafka Streams
部署方式 独立集群(JobManager + TaskManager) 嵌入应用,无需单独集群
状态管理 支持 RocksDB、FsState、Memory 三种后端 基于 RocksDB 的本地状态存储
容错机制 Checkpoint + Savepoint,精确一次语义 基于 Kafka 的 offset 提交,至少一次语义
时间语义 事件时间、摄入时间、处理时间 事件时间、处理时间
CDC 支持 原生 Flink CDC 连接器,功能丰富 需要自己解析 Debezium 消息
学习曲线 较陡,需要理解分布式计算概念 较平缓,熟悉 Kafka 即可上手
适用场景 复杂 ETL、实时数仓、大规模状态计算 轻量级流处理、微服务集成

3.3.1 我的选型建议

说实话,没有银弹。我根据项目经验总结了几条:

  • 如果你要做实时血缘捕获,而且数据源是 MySQL/Oracle 等关系型数据库,我建议用 Flink + Flink CDC。它原生支持 Schema 变更、动态加表,省很多事。
  • 如果你的数据已经在 Kafka 里,而且血缘逻辑比较简单(比如字段映射),Kafka Streams 就够了。部署简单,运维成本低。
  • 如果你需要精确一次语义,比如金融场景,Flink 更靠谱。Kafka Streams 的至少一次语义在某些场景下会丢数据。

注意:Kafka Streams 的状态存储在本地磁盘,如果任务重启后 Kafka 的 offset 和本地状态不一致,可能导致数据重复或丢失。Flink 的 Checkpoint 机制在这方面更成熟。

3.4 知识体系总览

下面这张图把本章的核心逻辑串起来了:

实时流处理框架知识体系 实时血缘捕获 Apache Flink Kafka Streams 核心概念:有界/无界流、时间语义 Flink CDC:binlog 解析、全量+增量 Checkpoint 容错、状态后端 轻量级库、嵌入应用 RocksDB 本地状态、至少一次语义 需自行解析 Debezium 消息 选型建议:场景驱动,没有银弹 复杂血缘 + 数据库 CDC → Flink;简单逻辑 + Kafka 生态 → Kafka Streams 精确一次语义场景优先选 Flink

这张图把 Flink 和 Kafka Streams 的定位、核心特性、选型建议都串起来了。你保存下来,以后做技术选型时可以拿出来对照。

3.5 本章小结

实时血缘捕获的底层引擎,Flink 和 Kafka Streams 是目前的主流选择。Flink 功能全面,适合复杂场景;Kafka Streams 轻量灵活,适合简单集成。我个人更倾向于 Flink,尤其是配合 Flink CDC 做数据库血缘捕获,体验确实好。但如果你团队 Kafka 玩得溜,业务逻辑也不复杂,Kafka Streams 完全够用。