Flink SQL 从本地安装到跑通第一条流式 SQL

1. 前置知识:只要会一点 SQL 就够了

Flink SQL 的一个重要设计目标就是:尽量兼容 ANSI-SQL 2011 标准

所以如果你之前用过:

  • MySQL、PostgreSQL 之类的数据库
  • 或者 Hive / Presto / ClickHouse 这类 SQL 引擎

那你学 Flink SQL 的感觉会非常顺滑。

本篇默认你至少知道这些 SQL 基础概念:

  • SELECT / FROM / WHERE
  • GROUP BY / COUNT / SUM 之类的聚合
  • CREATE TABLE 这种 DDL

不会 Java、不会 Scala 都没关系,照样能跟着做完。

2. 安装 Flink:本地起一个迷你集群

2.1 下载与本地安装

Flink 支持多种部署方式(Standalone、YARN、K8s 等),但入门最推荐的是:

直接下载官方二进制包,在本机跑一个 Standalone 集群。

你可以到官网下载对应版本的二进制包,然后解压:

bash 复制代码
tar -xzf flink-*.tgz
cd flink-*

2.2 启动本地集群

在 Flink 安装目录执行:

bash 复制代码
./bin/start-cluster.sh

如果一切正常,你会看到类似日志提示,表示:

  • JobManager 已经在本机启动;
  • TaskManager 也跟着起来了一台或多台。

然后打开浏览器访问:

http://localhost:8081

这就是 Flink 的 Web UI,你可以在上面看到:

  • 当前有哪些 Job 在跑;
  • 集群里有哪些 TaskManager;
  • 各个算子、子任务的运行状态等。

3. SQL Client:你的交互式 SQL 控制台

Flink 提供了一个命令行工具:SQL Client,非常适合:

  • 交互式写 SQL、调试;
  • 临时跑一个试验任务;
  • 或者让数据分析同学直接写实时 SQL。

在 Flink 安装目录执行:

bash 复制代码
./bin/sql-client.sh

如果启动成功,你会看到类似:

text 复制代码
Flink SQL>

这就是我们的交互式 SQL 环境,可以开始敲查询了。

4. Hello World:用 SQL 打印一行结果

第一条 SQL 很仪式感:

sql 复制代码
SELECT 'Hello World';

在 SQL Client 中执行后,你会看到类似输出:

text 复制代码
+-------------+
| Hello World |
+-------------+
| Hello World |
+-------------+
1 row in set

恭喜,你已经成功跑起了 Flink SQL。

虽然现在还没连数据源,但客观上,你已经用「SQL 写了一个 Flink 程序」------只不过输入是一个常量。

执行:

sql 复制代码
SHOW FUNCTIONS;

你会得到一长串函数名称,包括:

  • 各种数学函数:ABS, CEIL, FLOOR, POWER ...
  • 字符串函数:CONCAT, SUBSTRING, LOWER, UPPER ...
  • 时间函数:CURRENT_TIMESTAMP, NOW, DATE_FORMAT ...
  • 以及聚合函数、窗口函数、系统函数等

这些函数基本就是你写 Flink SQL 的「积木」。

举个例子,当前时间可以直接查询:

sql 复制代码
SELECT CURRENT_TIMESTAMP;

输出的是 SQL Client 所在机器的系统时间。

和传统数据库不同,Flink SQL 的表不是存储在 Flink 里的,而是:

Flink 通过 connector 连接到外部系统(Kafka、文件系统、数据库等),把它们映射成一张张「动态表」。

这些「外部表」就是我们在 SQL 里 FROM 后面写的表。

Flink SQL 里所有计算的入口就是 源表(Source Table)

5.1 用 DDL 创建一张 CSV 源表

假设我们有一个 CSV 文件 /path/to/something.csv,内容是公司员工信息:

  • emp_id:员工 ID
  • name:员工姓名
  • dept_id:部门 ID

我们可以用如下 DDL 把这个文件映射成一张表:

sql 复制代码
CREATE TABLE employee_information (
    emp_id  INT,
    name    VARCHAR,
    dept_id INT
) WITH ( 
    'connector' = 'filesystem',
    'path' = '/path/to/something.csv',
    'format' = 'csv'
);

解释一下:

  • CREATE TABLE employee_information (...):定义表名和字段;

  • WITH (...):告诉 Flink 这张表的「背后」是什么系统:

    • connector = 'filesystem' 表示这是文件系统上的一个文件;
    • path 指定文件路径;
    • format = 'csv' 表明文件格式为 CSV。

从现在开始,你就可以像查数据库一样查这张"表"了。

6. 连续查询(Continuous Queries):SQL 在流上的真正语义

你在数据库里写 SELECT,通常是:

查一下当前时刻表里有哪些数据,然后查询就结束了。

而在 Flink 里,如果这张表的底层是一个流式输入,例如 Kafka topic:

同样一条 SELECT,在 Flink 的流模式下是"永远跑不完"的。

Flink 把这种一直在消费新数据、不断更新结果的 SELECT,叫做:

Continuous Query(连续查询)

这类查询的结果不是一张静态表,而是一个会持续变化的 动态表(Dynamic Table)

7. 示例:过滤某个部门的员工

回到刚才那张 employee_information 表,假设我们只关心部门 ID 为 1 的员工:

sql 复制代码
SELECT *
FROM employee_information
WHERE dept_id = 1;

在「批」的场景下,这就是读取 CSV 文件中所有行,筛出 dept_id = 1 的记录然后结束。

如果把底层换成一个永远写入新行的文件或流式文件源,同样的 SQL 就会变成:

  • 每有新记录写入文件;
  • Flink 立刻读取、判断 dept_id 是否为 1;
  • 满足条件的记录即时输出到结果。

这就是 SQL 写「流式过滤」的最简版本。

我们再看一个常见需求:统计每个部门当前有多少员工。

sql 复制代码
SELECT 
   dept_id,
   COUNT(*) AS emp_count
FROM employee_information
GROUP BY dept_id;

在数据库里,这只是对当前静态数据执行一次聚合。

但在 Flink 的流式语义下,这条 SQL 的含义是:

随着新员工不断加入 / 部门调整,这个统计结果会持续被更新。

这就带来一个关键点:这是一个有状态(stateful)的查询

  • COUNT(*) 不是一次算完,而是要持续维护每个 dept_id 的计数;
  • Flink 会把这些状态存储在自己的内部 state backend(例如 RocksDB / 内存)中;
  • 借助 checkpoint + 事务 sink,它还能做到 端到端 Exactly-Once
  • 即便机器宕机重启,状态也能从 checkpoint 恢复,保证最终结果正确。

这也是为什么 Flink 非常适合做:

  • 实时指标聚合;
  • 实时看板;
  • 实时风控 / 实时行为统计等场景。

9. Sink 表(Sink Tables):把结果写出去

在 SQL Client 里,你可以看到查询结果实时打印出来,但是:

这些输出只是 "读给你看",不会持久化到某个系统里。

如果你想:

  • 把统计结果写到 Kafka 做下游消费;
  • 写到数据库供报表系统查询;
  • 或者写成 HDFS/OSS 上的离线分区表;

就需要定义一张 Sink 表(目标表) ,然后配合 INSERT INTO 使用。

9.1 定义一个统计结果表

例如定义部门员工数统计结果表 department_counts

sql 复制代码
CREATE TABLE department_counts (
   dept_id    INT,
   emp_count  BIGINT
) WITH (
   'connector' = 'kafka',
   'topic' = 'department_counts',
   'properties.bootstrap.servers' = 'kafka:9092',
   'format' = 'json'
);

9.2 用 INSERT INTO 提交一个实时任务

然后我们就可以这么写:

sql 复制代码
INSERT INTO department_counts
SELECT 
   dept_id,
   COUNT(*) AS emp_count 
FROM employee_information
GROUP BY dept_id;

这条 INSERT INTO ... SELECT ... 语句的行为是:

  • 在 SQL Client 中执行后,Flink 会把它 以一个 Job 的形式提交到集群

  • 这个 Job 会:

    • 持续消费 employee_information 底层的数据;
    • 维护每个 dept_id 的人数统计;
    • 按 Flink 的 changelog 语义把结果写入 department_counts(Kafka topic 或其它 sink)。

此时查询不再只是"打印在控制台",而是:

真正变成了一个 在线运行的流式任务,持续为你的报表 / 大屏 / 风控服务数据。

10. 卡住了怎么办?官方社区与更多学习资源

如果你在操作过程中遇到问题,可以直接求助官方社区。

文档里特别提到:

  • Apache Flink 的用户邮件列表是活跃度非常高的社区资源;
  • 遇到奇怪报错、connector 问题、SQL 语法疑问,都可以去那里提问。

此外,Flink 官方文档也提供了很多后续阅读方向:

  • SQL: 支持的操作与语法细节(各类函数、Joins、窗口等);
  • SQL Client: 如何不写任何 Java/Scala 代码,直接用 SQL 提交作业到集群;
  • Concepts & Common API: Table API / SQL 的共享概念,比如 Dynamic Table、Time Attribute 等;
  • Streaming Concepts: 针对流式场景的特殊概念,如水位线、更新结果的处理;
  • Built-in Functions: 所有内置函数的完整列表和参数说明;
  • Connect to External Systems: 支持的连接器列表(Kafka、FileSystem、JDBC、Iceberg、Paimon、Upsert-Kafka 等)。

11. 小结:下一步可以怎么玩?

通过这一篇,你已经知道了:

  1. 如何在本地启动一个 Flink 集群
  2. 如何用 SQL Client 交互式写查询
  3. 如何用 DDL 定义 源表(从 CSV / Kafka 等读取)
  4. 如何理解 连续查询 + 动态表 的流式语义
  5. 如何用 INSERT INTO 把结果写入 Sink 表,变成真正的流式任务

你现在可以尝试:

  • 把 CSV 换成 Kafka 源表
  • 把简单的 GROUP BY 换成 时间窗口(TUMBLE / HOP / SESSION);
  • 在结果表里加上窗口开始结束时间,做一个实时的部门人数监控。
相关推荐
武子康1 小时前
大数据-173 Elasticsearch 映射与文档增删改查实战(基于 7.x/8.x)JSON
大数据·后端·elasticsearch
前进的李工1 小时前
SQL排序与分页查询技巧
开发语言·数据库·sql·mysql·oracle
菜鸟冲锋号1 小时前
Paimon 流 - 流增量关联(CDC 模式)具体实现方案
大数据·flink·数据湖·paimon·多流外键关联
熙梦数字化1 小时前
企业资源计划(ERP)系统是什么?有哪些特点?
大数据·人工智能·erp
无代码专家1 小时前
数字化转型下的订单管理全流程优化方案
大数据·运维·人工智能
二进制_博客1 小时前
Flink doesn‘t support ENFORCED mode for PRIMARY KEY constraint
大数据·flink·flinkcdc
Hello.Reader1 小时前
用 Flink SQL 搭建一个实时统计应用Kafka → Flink → MySQL 实战
sql·flink·kafka
路边草随风1 小时前
java 实现 flink 读 kafka 写 delta
java·大数据·flink·kafka
jiayong231 小时前
Elasticsearch 分词器完全指南:原理、类型与实战
大数据·elasticsearch·搜索引擎