3. Flink 核心概念:DataStream API、时间语义与 Watermark 机制
说实话,很多刚接触 Flink 的同学,都会被这三个概念绕晕。我当年第一次搭实时特征平台时,也踩过 Watermark 的坑。今天咱们就把它们掰开揉碎了讲清楚。
3.1 DataStream API:你的数据管道
DataStream API 是 Flink 最核心的编程接口。说白了,它就是一套让你能像操作集合一样,去处理无界数据流的工具集。
我个人习惯把 DataStream 想象成一条传送带。数据从源头(Source)上来,经过各种算子(Operator)加工,最后送到下游(Sink)。
- map:一对一转换,比如把 JSON 解析成 Java 对象
- flatMap:一对多转换,比如把一行日志拆成多个事件
- filter:过滤掉不需要的数据
- keyBy:按某个字段分组,后续才能做聚合
- window:划定一个时间范围做计算
举个例子,我在做用户行为特征时,经常这么写:
DataStream<String> sourceStream = env.addSource(new KafkaSource<>());
DataStream<UserEvent> parsedStream = sourceStream
.map(json -> JsonUtil.parse(json, UserEvent.class));
DataStream<UserEvent> validStream = parsedStream
.filter(event -> event.getUserId() != null);
validStream
.keyBy(UserEvent::getUserId)
.window(TumblingEventTimeWindows.of(Time.minutes(5)))
.aggregate(new UserFeatureAggregator())
.addSink(new FeatureStoreSink());
你看,整个流程非常直观。每个算子只做一件事,组合起来就是一条完整的数据管道。
3.2 时间语义:Event Time vs Processing Time
这是 Flink 里最容易搞混的概念。我刚开始做实时特征时,就因为这个吃过亏。
| 维度 | Event Time | Processing Time |
|---|---|---|
| 定义 | 事件实际发生的时间 | 数据到达 Flink 算子的时间 |
| 准确性 | 高,反映真实业务时序 | 低,受系统延迟影响 |
| 延迟 | 需要等待迟到数据 | 无等待,立即处理 |
| 适用场景 | 对时序敏感的特征计算 | 实时监控、简单聚合 |
为什么会这样?你想想看,用户点击发生在 10:00,但网络抖动导致数据 10:05 才到。如果用 Processing Time,这个点击就被算到 10:05 的窗口里了。对于特征平台来说,这会导致特征值错乱。
所以,实时特征平台几乎只用 Event Time。我参与过的几个项目,无一例外。
3.3 Watermark 机制:解决乱序问题的关键
Watermark 是 Flink 里最巧妙的设计之一。它解决了一个核心矛盾:既要等迟到数据,又不能无限期等。
Watermark 本质上是一个时间戳,它告诉 Flink:「在这个时间戳之前的数据,我已经都收到了。没到的,就当它丢了吧。」
3.3.1 Watermark 的生成方式
Flink 提供了两种内置的 Watermark 生成策略:
- 周期性生成(Periodic):每隔一段时间生成一次 Watermark。适合数据量大的场景。
- 按事件生成(Punctuated):每来一条数据就判断是否生成。适合对延迟敏感的场景。
我个人习惯用周期性生成,性能更可控。代码大概长这样:
DataStream<UserEvent> streamWithWatermark = inputStream
.assignTimestampsAndWatermarks(
WatermarkStrategy.<UserEvent>forBoundedOutOfOrderness(Duration.ofSeconds(10))
.withTimestampAssigner((event, timestamp) -> event.getEventTime())
);
这里 forBoundedOutOfOrderness(Duration.ofSeconds(10)) 的意思是:允许数据最多迟到 10 秒。超过这个范围的数据,会被当作迟到数据丢弃或侧输出。
3.3.2 Watermark 与窗口的联动
Watermark 和窗口的关系,可以用一张图来理解:
从图上可以看得很清楚:当 Watermark 到达 00:10 时,Flink 会触发 [00:00, 00:05) 这个窗口的计算。因为 Watermark 已经超过了窗口的结束时间 00:05,说明这个窗口的数据基本到齐了。
3.4 迟到数据处理策略
即使有了 Watermark,还是会有数据迟到。怎么办?Flink 给了三种选择:
- 直接丢弃:默认行为,简单粗暴。
- 侧输出(Side Output):把迟到数据单独收集起来,后续做补偿处理。我比较推荐这种方式。
- 允许延迟(Allowed Lateness):窗口触发后,再等一段时间。期间每来一条迟到数据,就重新触发一次窗口计算。
3.5 小结
这三个概念是 Flink 实时计算的基石。DataStream API 是操作工具,Event Time 是时间基准,Watermark 是乱序问题的解决方案。理解透它们,你就能搭建出准确、高效的实时特征管道。
嗯,我记得刚带团队时,有个同事把 Processing Time 和 Event Time 混用了,结果特征值对不上。排查了两天才找到原因。从那以后,我们团队就定了个规矩:所有特征计算必须用 Event Time + Watermark,没有例外。