2. Flink 基础回顾:架构、API、时间语义与 Watermark

在进入实时特征计算与因子库结合之前,必须夯实 Flink 核心基础。本章聚焦 Flink 运行时架构、DataStream API 编程模型、Event Time / Processing Time 两种时间语义,以及 Watermark 机制——这是构建低延迟、高准确率实时特征 pipeline 的基石。

2.1 Flink 运行时架构

Flink 采用 主从(Master-Worker) 架构,核心组件包括 JobManager、TaskManager 和 Client。作业提交后,Client 将 JobGraph 发送给 JobManager,JobManager 负责调度、检查点协调与故障恢复;TaskManager 是实际执行任务的 Worker,每个 TaskManager 包含多个 Task Slot,Slot 是资源调度的最小单位。

架构要点: JobManager 不参与数据流处理,只做元数据管理与协调;TaskManager 之间通过 Netty 进行数据交换,避免单点瓶颈。
Flink 运行时架构 Client JobManager TaskManager 1 Slot 1 Slot 2 TaskManager 2 Slot 3 Slot 4 TaskManager 3 Slot 5 Slot 6 JobManager 负责调度与容错;TaskManager 通过 Slot 隔离资源,并行执行算子

2.2 DataStream API 核心概念

DataStream API 是 Flink 实时处理的主要编程接口。核心抽象包括 Source(数据源)、Transformation(转换算子)和 Sink(输出端)。在因子库场景中,Source 通常对接 Kafka,Transformation 实现特征计算逻辑,Sink 写入 Redis / HBase / ClickHouse。

类别 常用算子 因子库典型用途
Source KafkaSource, FileSource, SocketSource 从 Kafka topic 读取行情、订单流
Transformation map, flatMap, filter, keyBy, window, process 因子计算、数据清洗、分组聚合
Sink KafkaSink, RedisSink, JDBCSink 因子结果写入在线存储或消息队列
编程提示: 在因子库中,keyBy 通常按股票代码或因子 ID 分区,确保同一标的的事件进入同一个算子实例,保证状态一致性。

2.3 时间语义:Event Time vs Processing Time

Flink 支持三种时间语义,其中 Event Time 和 Processing Time 是实时特征计算中最常用的两种。选择哪种时间语义直接决定因子计算的准确性与延迟。

  • Processing Time:算子所在机器的系统时钟。延迟最低,但结果依赖数据到达顺序,不可复现。适用于对精度要求不高的实时监控指标。
  • Event Time:事件实际发生的时间戳(通常嵌入在数据中)。结果可复现,能正确处理乱序数据,但需要配合 Watermark 机制。因子库中绝大多数特征(如过去 5 分钟均价、累计成交量)必须使用 Event Time。
常见误区: 使用 Processing Time 做滑动窗口因子(如 30 秒 VWAP)会导致不同批次计算结果不一致,无法回测验证。因子库生产环境必须默认启用 Event Time。
Event Time vs Processing Time 对比 Processing Time 数据到达时间 = 处理时间 t1: 事件A (10:00:01) → 处理 t2: 事件B (09:59:58) → 处理 乱序导致窗口结果错误 不可复现,无法回测 Event Time 按事件实际发生时间处理 事件A (10:00:01) → 10:00 窗口 事件B (09:59:58) → 09:59 窗口 乱序不影响窗口归属 可复现,支持回测

2.4 Watermark 机制

Watermark 是 Flink 中衡量 Event Time 进度的核心机制,本质是一个 单调递增的时间戳,表示“当前时间之前的数据已经全部到达”。当 Watermark 超过窗口结束时间时,触发窗口计算并输出结果。

2.4.1 Watermark 生成策略

Flink 提供两种内置 Watermark 生成器:

  • 周期性生成(Periodic):每隔一定时间(如 100ms)生成一次 Watermark,适用于大部分实时场景。
  • 断点式生成(Punctuated):每条数据到达时判断是否生成 Watermark,适用于数据稀疏或特殊标记场景。
// 周期性 Watermark 生成示例(允许 5 秒乱序)
DataStream<Event> stream = env
    .fromSource(source, WatermarkStrategy
        .<Event>forBoundedOutOfOrderness(Duration.ofSeconds(5))
        .withTimestampAssigner((event, timestamp) -> event.getEventTime()),
        "Kafka Source");

2.4.2 Watermark 与窗口的交互

窗口类型 触发条件 因子库场景
Tumbling Window Watermark >= 窗口结束时间 整点统计因子(如每分钟累计成交额)
Sliding Window Watermark >= 每个滑动窗口结束时间 滚动特征(如过去 5 分钟、10 分钟均价)
Session Window Watermark >= 会话间隔 + 最后事件时间 用户行为间隔因子
生产建议: 因子库中乱序容忍度(outOfOrderness)通常设置为 2~10 秒,具体取决于上游 Kafka 延迟抖动。设置过小会导致窗口频繁延迟触发,设置过大会增加状态存储压力。

2.5 本章小结