Flink流处理中的表

将一个Table转换成DataStream时,有仅插入流(Insert-Only-Streams)、更新日志流(Changelog Streams)两种方式,具体使用哪种方式取决于表中是否存在更新操作

文章目录

        • 动态表和持续查询
          • 1、动态表
          • 2、持续查询
          • 3、将流转换成动态表
        • 用SQL持续查询
          • 1、更新查询
          • 2、追加查询
          • 3、查询限制
        • 将动态表转换成流

关系型表/SQL 流处理
处理的数据对象 字段元组的有界集合 字段元组的无限序列
查询(Query) 可以访问到完整的数据输入 无法访问到所有数据,必须持续等待流式输入
查询终止条件 生成固定大小的结果集后终止 永不停止,根据持续收到的数据不断更新查询结果
动态表和持续查询
1、动态表

当流中有新数据到来,初始的表中会插入一行,基于这个表定义的SQL查询,就应该在之前的基础上更新结果,这样得到的表就会不断地动态变化,因此被称为动态表(Dynamic Tables)。动态表是Flink在Table API和SQL中的核心概念,它为流数据处理提供了表和SQL支持。

动态表的概念,我们在传统的关系型数据库中已经有所接触了,数据库中的表,是一系列INSERT、UPDATE和DELETE语句执行的结果,在关系型数据库中,一般把它称为更新日志流(changelog stream)。当我们保存了表在某一时刻的快照,那么接下来只要读取更新日志流,就可以得到表之后的变化过程和最终结果。在很多关系型数据库(如Oracle、DB2)中都有物化视图(Materialized Views)的概念,可以用来缓存SQL查询的结果,它的更新就是不停地处理更新日志流的过程

2、持续查询

数据再不断变化,基于它定义的SQL查询也就永远不会停止,一致随着新数据的到来而继续执行,这样的查询被称作持续查询。

对动态表定义的查询操作,都是持续查询;而持续查询的结果也会是一个动态表

Flink流处理中的表_第1张图片

持续查询的步骤:

  • 1、流被转换为动态表
  • 2、对动态表进行持续查询,生成新的动态表
  • 3、生成的动态表被转换成流

只要API将流和动态表的转换封装起来,就可以直接在数据流上执行SQL查询,用处理表的方式来做流处理

3、将流转换成动态表

为了能够使用SQL做流处理,必须先把流转换成动态表。如果把流看作一张表,那么流中每个数据的到来都看作是对表的一次插入操作,在表的末尾添加一行数据。因为流式连续不断的,而且之前的输出结果无法改变、只能在后面追加,所以我们其实是通过一个只有插入操作(insert-only)和更新日志流,来构建一个表

Flink流处理中的表_第2张图片

用SQL持续查询
1、更新查询

在代码中定义一个SQL查询:

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

原始的动态表注册为EventTable,经过查询转换后得到urlCountTable,动态表中有两个字段

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

Flink流处理中的表_第3张图片

当原始动态表不停插入新的数据时,查询得到的urlCountTable会持续地进行更改,由于count数量可能会叠加增长,因此这里的更改操作可以是简单的插入,也可以是对之前数据的更新。

上述查询结果表的插入与更新的具体步骤如下:

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

当我们执行一个简单的条件查询,结果表中就会像原始表EventTable一样,只有插入操作了

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

这样的持续查询,就被称为追加查询(Append Query),它定义的结果表的更新日志流只有INSERT操作。追加查询得到的结果表

更新查询的判断标准:结果表中的数据是否会有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操作

Flink流处理中的表_第4张图片

3、查询限制

在实际应用中,有些持续查询会因为计算代价太高而受到限制。代价太高指的是:需要维护的状态持续增长、更新数据的计算太复杂

  • 状态大小:用持续查询做流处理,往往会运行至少几周到几个月;所以持续查询处理的数据总量可能非常大
  • 更新计算:对于有些查询来说,更新计算的复杂度很高,每来一条新的数据,更新结果的时候可能需要全部重新计算,并且对很多已经输出的行进行更新。典型的例子如RANK()函数,基于一组数据计算当前值的排名
将动态表转换成流

动态表可以通过插入、更新、删除操作,进行持续地更改。将动态表转换为流或其将其写入外部系统时,就需要对这些更改操作进行编码,通过发送编码消息的方式告诉外部系统要执行的操作。在Flink中,Table API和SQL支持三种编码方式

  • 仅追加流(Append-only):仅通过插入更改来修改的动态表,可以直接转换为仅追加流。流中发出的数据对应动态表中新增的每一行

  • 撤回流(Retract):包含两类消息的流,添加消息和撤回消息

    (具体的编码规则:INSERT插入操作编码为add消息;DELETE删除操作编码为retract消息;而UPDATE更新操作为更改行的retract消息,再add消息)

    Flink流处理中的表_第5张图片

这里我们用+代表add,-代表retract。

1、当 Alice 的第一个点击事件到来时,结果表新增一条数据[Alice, 1]
2、而当 Alice 的第二个点击事件到来时,结果表会将[Alice, 1]更新为[Alice, 2],对应的编码就是删除[Alice, 1],插入[Alice, 2]
  • 更新插入流(Upsert):包含两类消息,更新插入消息和删除消息

    (upsert是updata、insert的合成词,所以对于更新插入流来说,INSERT插入操作和UPDATE更新操作,统一编码为upsert消息;而DELETE删除操作则编码为delete消息)

    Flink流处理中的表_第6张图片

动态表中必须有唯一的键,通过这个key进行查询,如果存在对应的数据就做更新,如果不存在就直接插入,这是一个动态表可以转换为更新插入流的必要条件。收到这条流数据的外部系统,也需要指定唯一的键,这样才能正确处理消息

更新插入流和撤回流的区别:更新操作由于有key的存在,只需要用单条消息编码就可以,效率更高

注意:在代码里将动态表转换为DataStream时,只支持仅追加和撤回流,调用toChangelogStream()得到的是撤回流

你可能感兴趣的:(Flink,flink)