Flink TableAPI和SQL(七)流转成动态表做动态查询

将流转换成动态表

如果把流看作一张表,那么流中每个数据的到来,都应该看作是对表的一次插入(Insert)操作,会在表的末尾添加一行数据。因为流是连续不断的,而且之前的输出结果无法改变、只能在后面追加;所以我们其实是通过一个只有插入操作(insert-only)的更新日志(changelog)流,来构建一个表。

当用户点击事件到来时,就对应着动态表中的一次插入(Insert)操作,每条数据就是表中的一行;随着插入更多的点击事件,得到的动态表将不断增长。

用 SQL 持续查询

更新(Update)查询

我们在代码中定义了一个 SQL 查询。

Table urlCountTable = tableEnv.sqlQuery("SELECT user, COUNT(url) as cnt FROMEventTable GROUP BY user");

这个查询很简单,主要是分组聚合统计每个用户的点击次数。我们把原始的动态表注册为EventTable,经过查询转换后得到 urlCountTable;这个结果动态表中包含两个字段,具体定义如下:

[
 user: VARCHAR, // 用户名
 cnt: BIGINT // 用户访问 url 的次数
]

当原始动态表不停地插入新的数据时,查询得到的 urlCountTable 会持续
地进行更改。由于 count 数量可能会叠加增长,因此这里的更改操作可以是简单的插入(Insert),也可以是对之前数据的更新(Update)。换句话说,用来定义结果表的更新日志(changelog)流中,包含了 INSERT 和 UPDATE 两种操作。这种持续查询被称为更新查询(Update Query),更新查询得到的结果表如果想要转换成 DataStream,必须调用 toChangelogStream()方法。

具体步骤解释如下:

  • 当查询启动时,原始动态表 EventTable 为空;
  • 当第一行 Alice 的点击数据插入 EventTable 表时,查询开始计算结果表,urlCountTable中插入一行数据[Alice,1]。
  • 当第二行 Bob 点击数据插入 EventTable 表时,查询将更新结果表并插入新行[Bob,1]。
  • 第三行数据到来,同样是 Alice 的点击事件,这时不会插入新行,而是生成一个针对已有行的更新操作。这样,结果表中第一行[Alice,1]
    就更新为[Alice,2]。
  • 当第四行 Cary 的点击数据插入到 EventTable 表时,查询将第三行[Cary,1]插入到结果表中。

追加(Append)查询

上面的例子中,查询过程用到了分组聚合,结果表中就会产生更新操作。如果我们执行一个简单的条件查询,结果表中就会像原始表 EventTable 一样,只有插入(Insert)操作了。

Table aliceVisitTable = tableEnv.sqlQuery("SELECT url, user FROM EventTable WHERE user = 'Cary'");

这样的持续查询,就被称为追加查询(Append Query),它定义的结果表的更新日志(changelog)流中只有 INSERT 操作。追加查询得到的结果表,转换成 DataStream 调用方法没有限制,可以直接用 toDataStream(),也可以像更新查询一样调用 toChangelogStream()。

这样看来,我们似乎可以总结一个规律:只要用到了聚合,在之前的结果上有叠加,就会产生更新操作,就是一个更新查询。但事实上,更新查询的判断标准是结果表中的数据是否会有 UPDATE 操作,如果聚合的结果不再改变,那么同样也不是更新查询。

什么时候聚合的结果会保持不变呢?一个典型的例子就是窗口聚合。
我们考虑开一个滚动窗口,统计每一小时内所有用户的点击次数,并在结果表中增加一个endT 字段,表示当前统计窗口的结束时间。这时结果表的字段定义如下:

[
 user: VARCHAR, // 用户名
 endT: TIMESTAMP, // 窗口结束时间
 cnt: BIGINT // 用户访问 url 的次数
]

与之前的分组聚合一样,当原始动态表不停地插入新的数据时,查询得到的结果 result 会持续地进行更改。比如时间戳在 12:00:00 到 12:59:59 之间的有四条数据,其中 Alice 三次点击、Bob 一次点击;所以当水位线达到 13:00:00 时窗口关闭,输出到结果表中的就是新增两条数据[Alice, 13:00:00, 3]和[Bob, 13:00:00, 1]。同理,当下一小时的窗口关闭时,也会将统计结果追加到 result 表后面,而不会更新之前的数据。

所以我们发现,由于窗口的统计结果是一次性写入结果表的,所以结果表的更新日志流中只会包含插入 INSERT 操作,而没有更新 UPDATE 操作。所以这里的持续查询,依然是一个追加(Append)查询。结果表 result 如果转换成 DataStream,可以直接调用 toDataStream()方法。

需要注意的是,由于涉及时间窗口,我们还需要为事件时间提取时间戳和生成水位线。

查询限制

在实际应用中,有些持续查询会因为计算代价太高而受到限制。所谓的“代价太高”,可能是由于需要维护的状态持续增长,也可能是由于更新数据的计算太复杂。
1) 状态大小

用持续查询做流处理,往往会运行至少几周到几个月;所以持续查询处理的数据总量可能非常大。例如我们之前举的更新查询的例子,需要记录每个用户访问 url 的次数。如果随着时间的推移用户数越来越大,那么要维护的状态也将逐渐增长,最终可能会耗尽存储空间导致查询失败。

SELECT user, COUNT(url)
FROM clicks
GROUP BY user;

2) 更新计算

对于有些查询来说,更新计算的复杂度可能很高。每来一条新的数据,更新结果的时候可能需要全部重新计算,并且对很多已经输出的行进行更新。一个典型的例子就是 RANK()函数,它会基于一组数据计算当前值的排名。例如下面的 SQL 查询,会根据用户最后一次点击的时间为每个用户计算一个排名。当我们收到一个新的数据,用户的最后一次点击时间(lastAction)就会更新,进而所有用户必须重新排序计算一个新的排名。当一个用户的排名发生改变时,被他超过的那些用户的排名也会改变;这样的更新操作无疑代价巨大,而且还会随着用户的增多越来越严重。

SELECT user, RANK() OVER (ORDER BY lastAction)
FROM (
 SELECT user, MAX(ts) AS lastAction FROM EventTable GROUP BY user
);
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一章 整体介绍 2 1.1 什么是 Table APIFlink SQL 2 1.2 需要引入的依赖 2 1.3 两种 planner(old & blink)的区别 4 第二章 API 调用 5 2.1 基本程序结构 5 2.2 创建环境 5 2.3 在 Catalog 中注册 7 2.3.1 Table)的概念 7 2.3.2 连接到文件系统(Csv 格式) 7 2.3.3 连接到 Kafka 8 2.4 查询 9 2.4.1 Table API 的调用 9 2.4.2 SQL 查询 10 2.5 将 DataStream 转换 11 2.5.1 代码达 11 2.5.2 数据类型与 Table schema 的对应 12 2.6. 创建临时视图(Temporary View) 12 2.7. 输出 14 2.7.1 输出到文件 14 2.7.2 更新模式(Update Mode) 15 2.7.3 输出到 Kafka 16 2.7.4 输出到 ElasticSearch 16 2.7.5 输出到 MySql 17 2.8 将转换 DataStream 18 2.9 Query 的解释和执行 20 1. 优化查询计划 20 2. 解释 DataStream 或者 DataSet 程序 20 第三章 流处理中的特殊概念 20 3.1 流处理和关系代数(,及 SQL)的区别 21 3.2 动态(Dynamic Tables) 21 3.3 流式持续查询的过程 21 3.3.1 将流转Table) 22 3.3.2 持续查询(Continuous Query) 23 3.3.3 将动态转换流 23 3.4 时间特性 25 3.4.1 处理时间(Processing Time) 25 3.4.2 事件时间(Event Time) 27 第四章 窗口(Windows) 30 4.1 分组窗口(Group Windows) 30 4.1.1 滚动窗口 31 4.1.2 滑动窗口 32 4.1.3 会话窗口 32 4.2 Over Windows 33 1) 无界的 over window 33 2) 有界的 over window 34 4.3 SQL 中窗口的定义 34 4.3.1 Group Windows 34 4.3.2 Over Windows 35 4.4 代码练习(以分组滚动窗口为例) 36 第五章 函数(Functions) 38 5.1 系统内置函数 38 5.2 UDF 40 5.2.1 注册用户自定义函数 UDF 40 5.2.2 标量函数(Scalar Functions) 40 5.2.3 函数(Table Functions) 42 5.2.4 聚合函数(Aggregate Functions) 45 5.2.5 聚合函数(Table Aggregate Functions) 47

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值