Flink动态表 (Dynamic Table)

传统的数据库SQL在设计时并未考虑流数据。但是结果,传统的数据库SQL处理与流处理之间在概念上几乎没有差距。

本文主要是想说一下Flink动态表的思路。主要是可以类比传统数据库的物化视图。
翻译于(官网)原地址:https://ci.apache.org/projects/flink/flink-docs-release-1.9/zh/dev/table/streaming/dynamic_tables.html#update-and-append-queries

数据流上的关系查询

传统的数据库SQL和实时SQL概念没差别,但是处理的差别还是很大的,这里简单列出一些区别:

传统数据库SQL处理实时流处理
传统数据库的表数据是有界限的实时数据无界限的
在批处理数据的查询是需要获取全量数据无法获取全量数据,必须等待新的数据输入
处理结束后就终止了利用输入的数据不断的更新它的结果表,绝对不会停止

尽管存在这些差异,使用关系查询和SQL来处理流并不是不可能的。高级关系数据库系统提供称为"物化视图"的功能。物化视图定义为SQL查询,就像常规虚拟视图一样。

与虚拟视图相比,物化视图缓存查询的结果,使得在访问视图时不需要执行查询。缓存的一个常见挑战是避免缓存提供过时的结果。物化视图在修改其定义查询的基表时会过时。Eager View Maintenance是一种在更新基表后立即更新实例化视图的技术。

如果我们考虑以下内容,Eager View Maintenance和流上的SQL查询之间的联系就变得很明显:

  • 数据库表是INSERT,UPDATE和DELETEDML语句流的结果,通常被称为更新日志流。
  • 物化视图定义为SQL查询。为了更新视图,查询需要持续处理视图源表的更改日志流。
  • 物化视图是流式SQL查询的结果。

考虑到这些要点,我们将在下面介绍动态表的以下概念。

动态表和连续查询

动态表是Flink的Table API和SQL对流数据的支持的核心概念。与静态表相比,动态表会随时间而变化,但可以像静态表一样查询动态表,只不过查询动态表需要产生连续查询。连续查询永远不会终止,会生成动态表作为结果表。查询不断更新其(动态)结果表以反映其(动态)输入表的更改。最终,动态表上的连续查询与定义物化视图的查询非常相似。

值得注意的是,连续查询的结果始终在语义上等同于在输入表的快照上执行批处理的到的相同查询结果。

下图显示了流,动态表和连续查询之间的关系:
动态表和连续查询之间的关系

  1. 数据流被转化为动态表
  2. 在产生的动态表上执行连续不断的查询,产生一个动态结果表。
  3. 结果动态表再次被转化为数据流。

注意:动态表首先是一个逻辑概念。在查询执行期间不一定(完全)实现动态表。

在下文中,会以schema如下的点击事件流来解释动态表和连续不断的查询。

[
  user:  VARCHAR,   // the name of the user
  cTime: TIMESTAMP, // the time when the URL was accessed
  url:   VARCHAR    // the URL that was accessed by the user
]

在流上定义Table

为了使用传统的关系查询处理流,必须将其转换为Table。从概念上讲,流的每个新增记录都被解释为对结果表的Insert操作。最终,可以理解为是在从一个INSERT-only changelog流上构建一个表。

下图可视化了click事件流(左侧)如何转换为表格(右侧)。随着插入更多点击流记录,结果表将持续增长。
在这里插入图片描述

注意:stream流上转化的表内部并没有被物化。

连续查询

在动态表上执行连续查询,并生成一个新的动态表作为结果。与批处理查询相反,连续查询永远不会终止并根据其输入表的更新来更新其结果表。在任何时间点,连续查询的结果在语义上都等同于在输入表的快照上以批处理模式执行同一查询的结果。

在下面的内容中,我们显示了对单击事件流上定义的clicks表的两个示例查询。

第一个查询是一个简单的GROUP-BY COUNT聚合查询。主要是对clicks表按照user分组,然后统计url得到访问次数。下图展示了clicks表在数据增加期间查询是如何执行的。
在这里插入图片描述
启动查询后,点击表(左侧)为空。当第一行插入到clicks表中时,查询开始计算结果表。插入第一行[Mary,./home]后,结果表(右侧,顶部)由单行[Mary,1]组成。当第二行[Bob,./cart]插入clicks表时,查询将更新结果表并插入新行[Bob,1]。第三行[Mary,./prod?id=1]产生已计算结果行的更新,从而将[Mary,1]更新为[Mary,2]。最后,当第四行添加到clicks表时,查询将第三行[Liz,1]插入结果表。

第二个查询与第一个查询类似,但是在对URL数量进行计数之前,除了将clicks表的user分组之外增加了一个1小时的滚动窗口(基于时间的计算(例如,窗口基于特殊的时间属性))。同样,该图显示了在不同时间点的输入和输出,以可视化动态表的变化性质。
在这里插入图片描述
和上面一样,输入表的点击次数显示在左侧。该查询每小时连续计算结果并更新结果表。clicks表包含四行,其时间戳记(cTime)在12:00:00和12:59:59之间。该查询从该输入计算两个结果行(每个用户一个),并将它们附加到结果表中。对于13:00:00和13:59:59之间的下一个窗口,clicks表包含三行,这将导致另外两行附加到结果表中。结果表会更新,因为随着时间的推移会将更多行添加到点击次数中。

Update 和 append 查询

尽管两个示例查询看起来非常相似(都计算了分组计数聚合),但是内部逻辑还是区别较大:

  • 第一个查询更新以前发出的结果,即结果表的更改日志流包含INSERT和UPDATE更改。
  • 第二个查询仅append到结果表,即结果表的更改日志流仅包含INSERT更改。

查询是生成仅append表还是update表有一些区别:

  • 产生update变化的查询通常必须维护更多状态。
  • 将仅append表转换为流与将update表的转换为流,方式不同。(可参考Table to Stream Conversion

查询限制

可以将许多但不是全部的语义有效查询评估为流中的连续查询。某些查询由于需要维护的状态大小或计算更新过于昂贵而无法计算。

  • 状态大小:连续查询在无界流上执行,通常应该运行数周或数月,甚至7*24小时。因此,连续查询处理的数据总量可能非常大。为了更新先前生成的结果,可能需要维护所有输出的行。例如,第一个示例查询需要存储每个用户的URL计数,以便能够增加计数,并在输入表收到新行时发出新结果。如果仅统计注册用户,则要维护的计数可能不会太高。但是,如果未注册的用户分配了唯一的用户名,则要维护的计数数将随着时间的推移而增长,最终可能导致查询失败。

    SELECT user, COUNT(url)
    FROM clicks
    GROUP BY user;
    
  • 计算更新:有时即使只添加或更新了单个输入记录,某些查询也需要重新计算和更新大部分发出的结果行。显然,这样的查询不适合作为连续查询执行。以下查询是一个示例,该查询根据最终点击时间为每个用户计算RANK。一旦clicks表收到新行,用户的lastAction就会更新,并且必须计算新的排名。但是,由于两行不能具有相同的排名,因此所有排名较低的行也需要更新。

    SELECT user, RANK() OVER (ORDER BY lastLogin)
    FROM (
      SELECT user, MAX(cTime) AS lastAction FROM clicks GROUP BY user
    );
    

Query Configuration”页面讨论了用于控制连续查询的执行的参数。某些参数可用于权衡维护状态的大小以提高结果的准确性。

Table转化为流

可以像常规数据库表一样通过INSERT,UPDATE和DELETE更改来连续修改动态表。它可能是一个具有一行且不断更新的表,一个没有UPDATE和DELETE修改的仅插入表,或介于两者之间的任何表。

将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码。Flink的Table API和SQL支持三种方式来编码动态表的更改:

  • Append-only stream:假如动态表的更改操作仅仅是insert ,那么变为stream就仅仅需要将插入的行发送出去即可。

  • Retract stream: 撤回流是具有两种消息类型的流,即添加消息和撤消消息。通过将INSERT更改编码为添加消息,DELETE更改编码为撤消消息,将UPDATE编码为对先前行的回撤消息和对新增行的增加消息,来完成将动态表转换为撤回流。下图可视化了动态表到撤回流的转换:
    在这里插入图片描述

Upsert流: Upsert流是具有两种消息类型的流,Upsert消息和Delete消息。转换为upsert流的动态表需要一个(可能是复合的)唯一键。通过将INSERT和UPDATE更改编码为upsert消息并将DELETE更改编码为delete消息,将具有唯一键的动态表转换为流。流算符需要知道唯一键属性才能正确处理消息。与撤回流的主要区别在于UPDATE更改使用单个消息进行编码,因此效率更高。下图显示了动态表到upsert流的转换。
在这里插入图片描述

Common Concepts”页面上讨论了将动态表转换为DataStream的API。请注意,将动态表转换为DataStream时仅支持Append-only streamRetract stream流。在TableSources和TableSinks页面上讨论了将动态表发送到外部系统的TableSink接口。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值