不懂Flink SQL这个…连写SQL的资格都没有

本文探讨了Flink SQL中的动态表(Dynamic Table)概念,解释了它们是如何在流处理中不断变化的,并通过Continuous Query进行连续查询。文章介绍了表中的Update和Delete操作,以及如何通过Append-only Stream、Retract Stream和Upsert Stream将表转换为流。此外,还讨论了支持时间操作的表,包括Process Time和Event Time在Flink SQL中的应用。

本篇要解决的问题:

  1. Flink SQL中的表和Spark SQL中的表有何区别?
  2. 当我们在Flink代码中写下一个SQL语句,它是怎么查询的?
  3. Append-only Stream、Retract Stream以及UpsertStream分别代表什么?
  4. Flink SQL中如果要基于Window做聚合,怎么实现?
  5. 在Flink SQL中的时间属性就是定义一个时间列吗?
  6. 请说明tableEnv.toAppendStream和tableEnv.toRetractStream的区别。

重新理解流处理中的表

表我们非常熟悉,因为最早我们接触RDBMS,学习MySQL、Oracle这种数据库的时候就有接触了。但在我们的认知中,表是一个有界的,我们一次就可以获取到全部数据的结构。

而在Flink中,其中核心是它的无界流处理, Flink也确实把有界流作为无界流的一种特例来去处理的。那大家来考虑,如果表的概念放在流中会是什么样的呢?如果大家真的去思考了,会有一堆的困惑。这些困惑不是我们写几行Flink SQL或者Table API就可以清楚的。

接下来,我就从概念层面来理解Flink SQL,这样我们将来才能更好地编写API,不至于出了问题根本不知道从哪儿下手。

不断变化的表和不间断的查询

在Flink中,它把针对无界流的表称之为Dynamic Table(动态表)。它是Flink Table API和SQL的核心概念。顾名思义,它表示了Table是不断变化的。

我们可以这样来理解,当我们用Flink的API,建立一个表,其实把它理解为建立一个逻辑结构,这个逻辑结构需要映射到数据上去。Flink source源源不断的流入数据,就好比每次都往表上新增一条数据。表中有了数据,我们就可以使用SQL去查询了。要注意一下,流处理中的数据是只有新增的,所以看起来数据会源源不断地添加到表中。

 

动态表也是一种表,既然是表,就应该能够被查询。我们来回想一下原先我们查询表的场景。

  1. 打开编译工具,编写一条SQL语句
  2. 将SQL语句放入到mysql的终端执行
  3. 查看结果
  4. 再编写一条SQL语句
  5. 再放入到终端执行
  6. 再查看结果
  7. …..如此反复

 

而针对动态表,Flink的source端肯定是源源不断地会有数据流入,然后我们基于这个数据流建立了一张表,再编写SQL语句查询数据,进行处理。这个SQL语句一定是不断地执行的。而不是只执行一次。注意:针对流处理的SQL绝对不会像批式处理一样,执行一次拿到结果就完了。而是会不停地执行,不断地查询获取结果处理。所以,官方给这种查询方式取了一个名字,叫Continuous Query,中文翻译过来叫连续查询。而且每一次查询出来的数据也是不断变化的。

这是一个非常简单的示意图。该示意图描述了:我们通过建立动态表和连续查询来实现在无界流中的SQL操作。大家也可以看到,在Continuous上面有一个State,表示查询出来的结果会存储在State中,再下来Flink最终还是使用流来进行处理。

 

所以,我们可以理解为Flink的Table API和SQL,是一个逻辑模型,通过该逻辑模型可以让我们的数据处理变得更加简单。而模型之下,是Dynamic Table和Continuous Query,这也是区分SQL批处理的重点。

表中会有Update和Delete吗?

我们前面提到的表示不断地Append,表的数据是一直累加的,因为表示对接Source的,Source是不会有update的。但如果我们编写了一个SQL。这个SQL看起来是这样的:

SELECT user, sum(money) FROM order GROUP BY user;

当执行一条SQL语句之后,这条语句的结果还是一个表,因为在Flink中执行的SQL是Continuous Query,这个表的数据是不断变化的。新创建的表存在Update的情况。仔细看下下面的示例,例如:

  1. 第一条数据,张三,2000,执行这条SQL语句的结果是,张三,2000
  2. 第二条数据,李四,1500,继续执行这条SQL语句,结果是,张三,2000 | 李四,1500
  3. 第三条数据,张三,300,继续执行这条SQL语句,结果是,张三,2300 | 李四,1500
  4. ….

大家发现了吗,现在数据结果是有Update的。张三一开始是2000,但后面变成了2300。

 

那还有删除的情况吗?

有的。看一下下面这条SQL语句:

SELECT t1.`user`, SUM(t1.`money`) FROM t_order t1

WHERE

         NOT EXISTS (SELECT T2.`user`AS TOTAL_MONEY FROM t_order t2 WHERE T2.`user` = T1.`user` GROUP BY t2.`user` HAVING SUM(T2.`money`) > 3000)

GROUP BY t1.`user`GROUP BY t1.`user`
  1. 第一条数据,张三,2000,执行这条SQL语句的结果是,张三,2000
  2. 第二条数据,李四,1500,继续执行这条SQL语句,结果是,张三,2000 | 李四,1500
  3. 第三条数据,张三,300,继续执行这条SQL语句,结果是,张三,2300 | 李四,1500
  4. 第四条数据,张三,800,继续执行这条SQL语句,结果是,李四,1500

 

惊不惊喜?意不意外?

因为张三的消费的金额已经超过了3000,所以SQL执行完后,张三是被处理掉了。从数据的角度来看,它不就是被删除了吗?

 

通过上面的两个示例,给大家演示了,在Flink SQL中,对接Source的表都是Append-only的,不断地增加。执行一些SQL生成的表,这个表可能是要UPDATE的、也可能是要INSERT的。

你真的懂表转换为流吗?

对表的编码操作

我们前面说到过,表是一种逻辑结构。而Flink中的核心还是Stream。所以,Table最终还是会以Stream方式来继续处理。如果是以Stream方式处理,最终Stream中的数据有可能会写入到其他的外部系统中,例如:将Stream中的数据写入到MySQL中。

 

我们前面也看到了,表是有可能会UPDATE和DELETE的。那么如果是输出到MySQL中,就要执行UPDATE和DELETE语句了。而DataStream我们在学习Flink的时候就学习过了,DataStream是不能更新、删除事件的。

 

如果对表的操作是INSERT,这很好办,直接转换输出就好,因为DataStream数据也是不断递增的。但如果一个TABLE中的数据被UPDATE了、或者被DELETE了,如果用流来表达呢?因为流不可变的特征,我们肯定要对这种能够进行UPDATE/DELETE的TABLE做特殊操作。

 

我们可以针对每一种操作,INSERT/UPDATE/DELETE都用一个或多个经过编码的事件来表示。

例如:针对UPDATE,我们用两个操作来表达,[DELETE] 数据+  [INSERT]数据。也就是先把之前的数据删除,然后再插入一条新的数据。针对DELETE,我们也可以对流中的数据进行编码,[DELETE]数据。

 

总体来说,我们通过对流数据进行编码,也可以告诉DataStream的下游,[DELETE]表示发出MySQL的DELETE操作,将数据删除。用 [INSERT]表示插入新的数据。

将表转换为三种不同编码方式的流

Flink中的Table API或者SQL支持三种不同的编码方式。分别是:

  1. Append-only流
  2. Retract流
  3. Upsert流

 

分别来解释下这三种流。

Append-only

跟INSERT操作对应。这种编码类型的流针对的是只会不断新增的Dynamic Table。这种方式好处理,不需要进行特殊处理,源源不断地往流中发送事件即可。

 

Retract

这种流就和Append-only不太一样。上面的只能处理INSERT,如果表会发生DELETE或者UPDATE,Append-only编码方式的流就不合适了。Retract流有几种类型的事件类型:

  1. ADD MESSAGE:这种消息对应的就是INSERT操作。
  2. RETRACT MESSAGE:直译过来叫取消消息。这种消息对应的就是DELETE操作。

我们可以看到通过ADD MESSAGE和RETRACT MESSAGE可以很好的向外部系统表达删除和插入操作。那如何进行UPDATE呢?好办!RETRACT MESSAGE + ADD MESSAGE即可。先把之前的数据进行删除,然后插入一条新的。完美~

 

Upsert

前面我们看到的RETR

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值