6.3 Flink Table API 与 SQL(概述)

Flink Table API 与 SQL 高层接口概览:StreamTableEnvironment 创建、Table 与 DataStream 双向转换、SQL 查询窗口聚合、Table API 与 DataStream API 的混合使用场景,以及本卷聚焦 DataStream 的声明。

为什么有 Table API/SQL

DataStream API 提供细粒度控制,但代码量较大。Table API/SQL 是声明式接口:

// DataStream API:20 行代码
stream.keyBy(Event::getSymbol)
    .window(TumblingEventTimeWindows.of(Duration.ofMinutes(1)))
    .aggregate(new CountAggregate());

// SQL:1 行
SELECT symbol, COUNT(*) FROM events 
GROUP BY symbol, TUMBLE(event_time, INTERVAL '1' MINUTE)

Table API/SQL 适合:

  • ETL 管道(数据转换)
  • 分析查询(聚合、Join)
  • 不熟悉 Java 的数据分析师

TableEnvironment

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

// 注册 Kafka 表
String createTable = """
    CREATE TABLE events (
        eventId STRING,
        symbol STRING,
        amount DOUBLE,
        -- v3 的 eventTime 是毫秒时间戳(JSON 里是数字),先按 BIGINT 读,
        -- 再算出一个 TIMESTAMP_LTZ 列给 Watermark 用
        eventTime BIGINT,
        event_time AS TO_TIMESTAMP_LTZ(eventTime, 3),
        WATERMARK FOR event_time AS event_time - INTERVAL '10' SECOND
    ) WITH (
        'connector' = 'kafka',
        'topic' = 'eventd-cleaned',
        'properties.bootstrap.servers' = 'kafka:9092',
        'properties.group.id' = 'eventd-sql',   -- Source 表必填
        'format' = 'json'
    )
    """;
tableEnv.executeSql(createTable);

SQL 查询

// 滚动窗口聚合(1.20 推荐的窗口 TVF 写法)
Table result = tableEnv.sqlQuery("""
    SELECT
        window_start,
        window_end,
        symbol,
        COUNT(*) as order_count,
        SUM(amount) as total_amount
    FROM TABLE(
        TUMBLE(TABLE events, DESCRIPTOR(event_time), INTERVAL '1' MINUTE))
    GROUP BY window_start, window_end, symbol
    """);

DataStream ↔ Table 转换

// DataStream → Table
DataStream<Event> stream = ...;
Table table = tableEnv.fromDataStream(
    stream,
    $("eventId"), $("symbol"), $("amount"), $("eventTime").rowtime());

// Table → DataStream:只接受 add-only 结果(窗口聚合属于这种)
DataStream<Row> resultStream = tableEnv.toDataStream(result);

// Table → 通用变更流:普通 GROUP BY 这类会更新结果、可能撤回旧行的查询必须用这个
DataStream<Row> changelogStream = tableEnv.toChangelogStream(result);

toDataStream 在遇到非 add-only 的结果时会抛出 TableException(提示只支持 insert-only 变更);toRetractStream 在 1.20 已标记废弃,新代码统一用 toChangelogStream。

fromDataStream 里的 $("eventTime").rowtime() 要求这个 DataStream 已经分配过时间戳和 Watermark;只加字段名、没有分配 Watermark 时,运行期会报「The stream must be timestamped and watermarked」。

混合使用

DataStream 和 Table 可以混合:

// 1. DataStream 读取 Kafka
DataStream<Event> stream = env.fromSource(kafkaSource, watermarkStrategy, "events");

// 2. 转为 Table 用 SQL 聚合
Table table = tableEnv.fromDataStream(stream, ...);
tableEnv.createTemporaryView("events", table);
Table aggregated = tableEnv.sqlQuery("SELECT ... FROM events GROUP BY ...");

// 3. 转回 DataStream 做 CEP
DataStream<Row> aggregatedStream = tableEnv.toDataStream(aggregated);
Pattern<Row, ?> pattern = Pattern.<Row>begin("start").where(...);
CEP.pattern(aggregatedStream, pattern).select(...);

本卷的立场

本卷主要使用 DataStream API,原因:

  1. 状态控制:DataStream 可以精确控制 ValueState/ListState
  2. CEP 模式:DataStream API 可直接使用 CEP 库;SQL 侧虽然有 MATCH_RECOGNIZE,但复杂模式、状态控制和与自定义算子的组合不如 Java CEP 灵活
  3. 自定义逻辑:eventd v3 的窗口逻辑和告警规则需要 Java 代码
  4. 与 Kafka Streams 衔接:DataStream 和 Kafka Streams 概念更接近

Table API/SQL 用于:

  • 快速原型验证
  • 简单的 ETL(数据格式转换)
  • 与分析师协作的查询

小结

API层级适用
SQL最高ETL、分析、快速查询
Table API中高声明式流处理
DataStream API低自定义状态、CEP、精细控制

本卷聚焦 DataStream API,Table API/SQL 仅作概述。需要深入了解可参考 Flink 官方文档。

第 6 章结束。下一节 7.1 连接器:Kafka Source/Sink 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 15.3 全书总结与学习路径回顾
  2. 15.2 响应式流处理的演进趋势
  3. 15.1 引擎选型指南