问题导读
1.动态表有什么特点?
2.流处理与批处理转换为表后有什么相同之处?
3.动态表和连续查询是什么关系?
4.连续查询本文列举了什么例子?
5.Flink的Table API和SQL支持哪三种编码动态表更改的方法?
由于Flink对流式数据的处理超越了目前流行的所有框架,所以非常受各大公司的欢迎,其中包括阿里,美团、腾讯、唯品会等公司。而当前也有很多的公司在做技术调研而跃跃欲试。
Flink为何如此受欢迎?
Flink难道比Spark还好用?在流式处理上,Flink是真正的流式处理,Spark则是将数据分割为微批处理。在设计上,Flink本身认为数据是流式的,批处理是流式处理的特殊情况。
动态表与传统表有什么不同?
在Flink中,流式数据和批数据都是可以转换为表的数据,然而流式数据转换为表,是比较难以理解的。所以流式数据会转换为动态表。那么动态表是怎么个情况?它与我们传统的关系表有什么相同之处,有什么不同之处?阅读下面内容可解决这些问题。
上一篇
彻底明白Flink系统学习24:【Flink1.7】Table API 和SQL API介绍4:数据类型映射表...
Flink的Table API和SQL支持是用于批处理和流处理的统一API。 这意味着Table API和SQL查询具有相同的语义,无论它们的输入是有界批量输入还是无界流输入。 因为关系代数和SQL最初是为批处理而设计的,所以关于无界流输入的关系查询不像有界批输入上的关系查询那样容易理解。
补充:
这里有一个比较难以理解的地方:关系代数是什么鬼?
关系代数是程序性查询语言,它将Relation作为输入并生成关系作为输出。 关系代数主要为关系数据库和SQL提供理论基础。由于外链有限制,如想了解更多可阅读原文
以下内容解释了Flink关于流数据的关系API的概念,流的配置参数等。
Streaming概念的整体介绍:
动态表:描述动态表的概念。
时间属性:解释时间属性以及表API和SQL中时间属性的处理方式。
连续查询中的连接:连续查询中不同支持的join类型。
时间表:描述时间表概念。
查询配置:列出表API和SQL特定的配置选项。
这里我们重点讲动态表。
动态表
SQL的设计并未考虑流数据。 因此,关系代数(和SQL)与流处理之间的概念差距很小。
本文讨论这些差异,并解释Flink如何在无界数据上实现与有界数据上的常规数据库引擎相同的语义。
数据流的关系查询
下表将传统的sql和流处理进行了比较。
SQL
流处理
关系(或表)是有界(多)元组的集合。流是无限的元组序列。对批处理数据(例如,关系数据库中的表)执行的查询可以访问完整的输入数据。流式查询在启动时无法访问所有数据,必须“等待”数据流入。批处理查询在生成固定结果后终止。流式查询会根据收到的记录不断更新其结果,并且永远不会完成。
尽管存在这些差异,但使用关系查询和SQL处理流并非不可能。 高级关系数据库系统提供称为物化视图的功能。 物化视图定义为SQL查询,就像常规虚拟视图一样。 与虚拟视图相比,物化视图缓存查询的结果,使得在访问视图时不需要评估查询性能。 缓存的一个常见挑战是阻止缓存提供过时的结果。 实例化视图在修改其定义查询的基表时会过时。 Eager View Maintenance是一种在更新基表后立即更新实例化视图的技术。
如果我们考虑以下内容,视图维护和流上的SQL查询之间的联系就变得很明显:
数据库表是INSERT,UPDATE和DELETE DML语句流的结果,通常称为changelog流。
物化视图定义为SQL查询。 为了更新视图,查询会持续处理视图基本关系的更新日志流。
物化视图是流式SQL查询的结果。
考虑到这些要点,我们将继续介绍动态表的以下概念。
动态表和连续查询
动态表是Flink的Table API和SQL支持流数据的核心概念。 与表示批处理数据的静态表相比,动态表随时间而变化。 可以像静态批处理表一样查询它们。 查询动态表会产生连续查询。 连续查询永远不会终止并生成动态表作为结果。 查询不断更新其(动态)结果表以反映其(动态)输入表的更改。 实质上,动态表上的连续查询与定义物化视图的查询非常相似。
值得注意的是,连续查询的结果始终在语义上等同于在输入表的快照上以批处理模式执行的相同查询的结果。这个比较绕,简单来说就是连续查询也是由状态的,一次查询跟批处理查询相比,执行方式和结果是相同的。
下图显示了流,动态表和连续查询的关系:
流转换为动态表。
在动态表上连续查询,生成新的动态表。
生成的动态表将转换回流。
注意:动态表首先是一个逻辑概念。 在查询执行期间,动态表不一定(完全)物化。
在下文中,我们将使用具有以下模式的单击事件流来解释动态表和连续查询的概念:
[Plain Text] 纯文本查看 复制代码
1
2
3
4
5
[
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
]
在流上定义表
为了使用关系查询处理流,必须将其转换为表。 从概念上讲,流的每个记录都被解释为对结果表的INSERT修改。 本质上,我们从INSERT更改日志流【changelog】构建表。
下图显示了click事件流(左侧)如何转换为表(右侧)。 随着更多点击流记录的插入,生成的表不断增长。
注意:在流上定义的表在内部未实现。
(1)连续查询
在动态表上计算连续查询,并生成新的动态表作为结果。 与批处理查询相反,连续查询永远不会,根据其输入表上的更新,终止并更新其结果表。 在任何时间点,连续查询的结果,在语义上等同于在输入表的快照上,以批处理模式执行的相同查询的结果。也就是说在某个时间点或则任意时间点上连续查询跟批查询结果是等同的。
在下文中,我们在点击事件流上定义的点击表上显示两个示例查询。
第一个查询是一个简单的GROUP-BY COUNT聚合查询。 它将点击表按user字段分组,并计算访问过的URL的数量。 下图显示了在使用其它行更新clicks表时,如何查询。
查询启动时,clicks表(左侧)为空。 当第一行插入到click表中时,查询开始计算结果表。 插入第一行[Mary,/ home]后,结果表(右侧,顶部)由一行[Mary,1]组成。 当第二行[Bob,/ car]插入到click表中时,查询将更新结果表并插入一个新行[Bob,1]。 第三行[Mary,./ prod?id = 1]产生已计算结果行的更新,以便[Mary,1]更新为[Mary,2]。 最后,当第四行附加到clicks击表时,查询将第三行[Liz,1]插入到结果表中。
第二个查询类似于第一个查询,但除了用户属性之外还在每小时翻滚窗口上对click表进行分组,然后计算URL的数量(基于时间的计算,例如窗口基于特殊时间属性,稍后讨论。) 同样,该图显示了不同时间点的输入和输出,以显示动态表的变化性质。
和以前一样,输入click表显示在左侧。 查询每小时连续计算结果并更新结果表。 click表包含四行,时间戳(cTime)在12:00:00和12:59:59之间。 查询从此输入计算两个结果行(每个用户一个)并将它们追加到结果表。 对于13:00:00到13:59:59之间的下一个窗口,单击(click)表包含三行,这导致另外两行被追加到结果表中。 结果表已更新,因为随着时间的推移会有更多行追加到点击(click)表。
(2)更新并追加查询
尽管两个示例查询看起来非常相似(都计算了分组计数聚合),但它们在一个重要方面有所不同:
第一个查询更新先前发出的结果,即定义结果表的更改日志流包含INSERT和UPDATE。
第二个查询仅追加到结果表,即结果表的更改日志流仅包含INSERT更改。
查询是生成仅追加表还是更新表含义:
产生更新的查询通常必须保持更多状态(下面查询限制)。
将仅追加表转换为流与更新表的转换不同(下面表到流转换)。
(3)查询限制
可以将许多(但不是全部)语义上有效的查询评估为流上的连续查询。 有些查询的计算成本太高,要么是因为需要维护的状态大小,要么是计算更新过于昂贵。
状态大小:连续查询在无界流上进行评估,通常应该运行数周或数月。 因此,连续查询处理的数据总量可能非常大。 必须更新先前发出的结果的查询需要维护所有发出的行,以便能够更新它们。 例如,第一个示例查询需要存储每个用户的URL计数,以便能够增加计数,并在输入表收到新行时发出新结果。 如果仅跟踪注册用户,则要维护的计数可能不会太高。 但是,如果未注册的用户分配了唯一的用户名,则要维护的计数数将随着时间的推移而增长,最终可能导致查询失败。
[SQL] 纯文本查看 复制代码
1
2
3
SELECT user, COUNT(url)
FROM clicks
GROUP BY user;
计算更新:即使只添加或更新了单个输入记录,某些查询也需要重新计算和更新大部分发出的结果行。 显然,这样的查询不适合作为连续查询执行。 一个示例是以下查询,该查询基于最后一次点击的时间为每个用户计算RANK。 只要click表接收到新行,就会更新用户的lastAction并且必须计算新的排名。 但是,由于两行不能具有相同的排名,因此所有排名较低的行也需要更新。
[SQL] 纯文本查看 复制代码
1
2
3
4
SELECT user, RANK() OVER (ORDER BY lastLogin)
FROM (
SELECT user, MAX(cTime) AS lastAction FROM clicks GROUP BY user
);
对于控制连续查询执行的参数。 一些参数可用于交换维持状态的大小以获得结果准确性。后面将会讨论,英语较好的老铁,可参考链接
表到流转换
INSERT,UPDATE和DELETE可以像常规数据库表一样持续修改动态表。 它可能是一个包含单行的表,它不断更新,只是一个没有UPDATE和DELETE的插入表,或者介于两者之间的任何内容。
将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码。 Flink的Table API和SQL支持三种编码动态表
改的方法:
(1)仅追加流(Append-only stream):只能通过INSERT更改的动态表可以通过提交插入的行转换为流。
(2)收回流(Retract stream):收回流是包含两种类型的消息的流,添加消息和收回消息。 将动态表转换为撤销流,通过将INSERT更改编码为添加消息,将DELETE更改编码为撤消消息,将UPDATE更改编码为更新(上一个)行的撤消消息以及添加消息更新新的行 。 下图显示了动态表到回收流的转换。
【对于上面更新可能难以理解,也就是更新是首先需要DELETE,然后INSERT,如下图图示中(mary,2)操作】
(3)Upsert流:upsert流是一种包含两种消息,upsert消息和删除消息的流。 转换为upsert流的动态表需要(可能是复合的)唯一键。 通过将INSERT和UPDATE编码为upsert消息并将DELETE更改为删除消息,将具有唯一键的动态表转换为流。 流运算需要知道唯一键属性才能正确应用消息。 与收回流的主要区别在于UPDATE使用单个消息((主键))进行编码,因此更有效。 下图显示了动态表到upsert流的转换。【如下图我们看到Upsert和Delete需要根据key操作】