1. 前置知识:只要会一点 SQL 就够了
Flink SQL 的一个重要设计目标就是:尽量兼容 ANSI-SQL 2011 标准 。
所以如果你之前用过:
- MySQL、PostgreSQL 之类的数据库
- 或者 Hive / Presto / ClickHouse 这类 SQL 引擎
那你学 Flink SQL 的感觉会非常顺滑。
本篇默认你至少知道这些 SQL 基础概念:
SELECT / FROM / WHEREGROUP 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 也跟着起来了一台或多台。
然后打开浏览器访问:
这就是 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 程序」------只不过输入是一个常量。
4.1 看看 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 所在机器的系统时间。
5. 源表(Source Tables):Flink 不存数据,只连数据
和传统数据库不同,Flink SQL 的表不是存储在 Flink 里的,而是:
Flink 通过 connector 连接到外部系统(Kafka、文件系统、数据库等),把它们映射成一张张「动态表」。
这些「外部表」就是我们在 SQL 里 FROM 后面写的表。
Flink SQL 里所有计算的入口就是 源表(Source Table)。
5.1 用 DDL 创建一张 CSV 源表
假设我们有一个 CSV 文件 /path/to/something.csv,内容是公司员工信息:
emp_id:员工 IDname:员工姓名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 写「流式过滤」的最简版本。
8. 连续聚合:为什么 Flink 需要维护状态
我们再看一个常见需求:统计每个部门当前有多少员工。
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. 小结:下一步可以怎么玩?
通过这一篇,你已经知道了:
- 如何在本地启动一个 Flink 集群
- 如何用 SQL Client 交互式写查询
- 如何用 DDL 定义 源表(从 CSV / Kafka 等读取)
- 如何理解 连续查询 + 动态表 的流式语义
- 如何用
INSERT INTO把结果写入 Sink 表,变成真正的流式任务
你现在可以尝试:
- 把 CSV 换成 Kafka 源表;
- 把简单的
GROUP BY换成 时间窗口(TUMBLE / HOP / SESSION); - 在结果表里加上窗口开始结束时间,做一个实时的部门人数监控。