3. DataStream API基础:DataStream与DataSet的统一、Source算子、Transformation算子概览

3.1 批流一体:从DataSet到DataStream的演进

在Flink 1.12之前,批处理使用DataSet API,流处理使用DataStream API,两套API在底层执行引擎、调度策略和状态管理上存在显著差异。这种割裂导致用户需要为批和流分别编写两套代码,维护成本高,且难以实现真正的“同一份代码,两种运行模式”。

Flink 1.12及之后版本正式实现了批流一体:DataSet API被标记为软弃用,所有批处理作业统一使用DataStream API编写,通过将批数据视为有界流,在运行时由调度器自动选择批模式或流模式执行。这一统一带来了以下核心收益:

  • API统一:批和流共用DataStream API,代码复用率提升80%以上。
  • 语义一致:Exactly-Once、Watermark、状态后端等机制在批和流中行为一致。
  • 运维简化:同一套监控、调优和部署流程覆盖所有作业。
对比维度 DataSet API(旧) DataStream API(统一后)
数据模型 有界数据集 有界/无界统一数据流
执行模式 批模式(Stage-by-Stage) 批模式 / 流模式自动切换
状态支持 有限(仅排序/聚合) 完整状态后端(RocksDB / Heap)
事件时间 不支持 完整Watermark + 事件时间语义
推荐状态 软弃用(Flink 1.18+) 官方推荐
实战建议:新项目直接使用DataStream API编写批处理逻辑。对于存量DataSet作业,建议按“Source替换 → Transformation迁移 → Sink替换”三步走策略平滑迁移,无需一次性重写全部代码。

3.2 Source算子:数据接入的三种典型方式

Source算子是DataStream的起点,负责从外部系统读取数据并转换为DataStream<T>。本节聚焦三种最常用的Source:文件、Kafka和Socket,覆盖离线批处理与实时流处理的核心场景。

3.2.1 文件Source(有界流)

文件Source适用于读取HDFS、S3、本地文件系统上的静态数据,是批处理场景的主力。Flink提供了readFile和FileSource两种方式,推荐使用FileSource(基于Source API v2,支持目录监控和格式扩展)。

// 推荐方式:FileSource(支持批流统一)
DataStream<String> textStream = env.fromSource(
    FileSource.forRecordStreamFormat(
        new TextLineInputFormat(),
        new Path("hdfs://cluster/data/events/2024/")
    )
    .monitorContinuously(Duration.ofHours(1))  // 流模式:每小时扫描新文件
    .build(),
    WatermarkStrategy.noWatermarks(),
    "file-source"
);

// 传统方式:readFile(仅批模式)
DataStream<String> batchStream = env.readTextFile("file:///data/input/");
参数 说明 典型值
monitorContinuously 启用持续监控模式,新文件到达后自动读取 Duration.ofHours(1)
FileEnumerator 文件发现策略(递归/非递归、正则过滤) RegexFileEnumerator
SplitReader 文件分片读取器(支持压缩格式) TextLineInputFormat

3.2.2 Kafka Source(无界流)

Kafka是实时特征工程中最常用的消息队列Source。Flink Kafka Connector支持精确一次语义、动态分区发现和消费偏移量管理。

Properties props = new Properties();
props.setProperty("bootstrap.servers", "kafka-broker:9092");
props.setProperty("group.id", "feature-group");

DataStream<String> kafkaStream = env.fromSource(
    KafkaSource.<String>builder()
        .setBootstrapServers("kafka-broker:9092")
        .setTopics("user-click", "user-purchase")
        .setGroupId("feature-group")
        .setStartingOffsets(OffsetsInitializer.earliest())
        .setValueOnlyDeserializer(new SimpleStringSchema())
        .setProperty("commit.offsets.on.checkpoint", "true")
        .build(),
    WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(5)),
    "kafka-source"
);
避坑指南:Kafka Source的并行度应小于等于Kafka Topic的分区数,否则多余SubTask会空转。建议并行度 = min(分区数, 集群可用Slot数)。

3.2.3 Socket Source(测试/原型)

Socket Source仅用于本地开发和快速原型验证,不适合生产环境(无持久化、无容错)。

DataStream<String> socketStream = env.socketTextStream("localhost", 9999, "\n");

3.3 Transformation算子概览:特征工程的核心操作

Transformation算子将Source产生的原始数据流转换为特征数据流。以下按单流操作和多流操作分类,列出特征工程中最常用的算子。

3.3.1 单流Transformation

算子 输入 → 输出 特征工程典型用途
map 1 → 1 字段提取、类型转换、缺失值填充
flatMap 1 → 0..n 日志解析(一行拆多条特征)、JSON展开
filter 1 → 0/1 异常值过滤、无效样本剔除
keyBy DataStream → KeyedStream 按用户ID/设备ID分组,为后续聚合做准备
reduce KeyedStream → DataStream 滑动窗口内特征累加(如近1小时点击次数)
process KeyedStream → DataStream 自定义状态管理、定时器触发特征计算

3.3.2 多流Transformation

算子 输入 → 输出 特征工程典型用途
union 多流 → 单流(同类型) 合并多个数据源(如点击流+曝光流)
connect / coMap 多流 → 单流(不同类型) 实时特征拼接(用户画像流 + 行为流)
join 多流 → 单流(窗口内关联) 订单流与支付流关联,生成交易特征
intervalJoin 多流 → 单流(时间区间关联) 用户登录前5分钟内的行为特征提取

3.4 批流一体Source与Transformation架构图

下图展示了批流一体架构下,Source算子如何将不同数据源统一接入DataStream,再经过Transformation算子链生成特征数据流,最终输出到Sink的全过程。

批流一体 Source → Transformation 架构图 文件 Source HDFS / S3 / 本地 Kafka Source Topic: user-click Socket Source localhost:9999 DataStream<String>(有界 / 无界统一) map / flatMap keyBy / reduce process(状态) join / union DataStream<FeatureRow>(特征数据流) Sink(HDFS / Kafka / Redis) 批模式:有界流 流模式:无界流

3.5 实战要点与最佳实践

Source选择矩阵:
• 离线批处理(T+1特征)→ 文件Source + 批模式执行
• 实时流处理(毫秒级特征)→ Kafka Source + 流模式执行
• 原型验证 / 单元测试 → Socket Source + 本地环境
Transformation链优化:Flink会自动合并相邻的一对一Transformation(如map → filter → map)为Operator Chain,减少序列化开销和网络传输。建议将轻量级转换操作连续编写,Flink会智能优化执行计划。