SQL动态表&连续查询大家好,我是老杨,今天给大家带来一篇关于FlinkSQL流式计算核心思想设计的文章。小伙伴们看下面之前,我们先来了解一下本文的整体思路。按照博主的思路,会更清晰:先分析一下把SQL应用到流处理的思路SQL应用到批处理的时候已经很成熟了。通过对比流批处理分析输入、数据处理、输出的异同,分析SQL应用于流处理需要解决的核心问题。SQL中如何使用SQL动态输入表技术将输入数据流映射到输入表的分析。分析如何使用SQL连续查询技术将计算逻辑映射到SQL中的操作语义使用SQL动态表&连续查询技术将流式SQL应用到两种常见情况,分析两种SQL连续查询:Update(更新)query&Append(附)查询分析如何使用SQL动态输出表技术将输出数据流映射到SQL中的输出表博主认为看完本节你应该掌握:SQL动态输入表、SQL动态输出表2.对应的查询两类SQL连续查询的场景和SQL语义1.将SQL应用到流处理的思路在流式SQL诞生之前,所有基于SQL的数据查询都是基于批数据,并没有将SQL应用到流中数据处理。所以如果我们要将SQL应用到流处理中,就必须站在巨人的肩膀上(批数据处理过程),那么具体的分析思路如下:第一步:首先比较批处理和流处理的异同点:如果有相同的部分,可以直接重复使用;差异是我们需要重点克服和关注的。步骤2:提取1中提到的差异,如果要满足这个差异,分析目前有哪些技术是相似的。第三步:从这些类似的技术进一步发展,以满足应用SQL流式任务的需求下面博主将按照以上三个步骤一步步介绍动态表诞生的背景,以及这个概念是如何诞生的。2、流批处理的异同点以及将SQL应用到流处理的核心所解决的问题。首先比较常见的批处理和流处理中数据源(输入表)、处理逻辑、数据汇(结果表)的异同。-输入表处理逻辑结果表批量处理静态表:有限的输入数据,是一个有界集合批量计算:每次执行查询都可以访问完整的输入数据,然后计算,输出完整的结果数据静态表:有限的数据流动态表的处理:无限输入数据,实时增加数据,连续流式计算:执行过程中无法访问完整的输入数据,每次计算的结果都是一个中间结果。动态表:与上面的流批处理相比无限数据之后,我们得到了将SQL应用到流式任务中需要解决的三个核心点:SQL输入表:分析如何将一个实时的、连续输入的流数据表示为输入表在SQL中。SQL处理与计算:分析将SQL查询逻辑转化为流式输入数据实时处理,进而生成流式输出数据的底层处理技术。SQL输出表:分析如何将SQL查询输出的连续数据流表示为SQL中的输出表。总结以上三点,引出了本节动态表和连续查询的两种技术方案:输出数据的时间处理技术3.SQL流处理的输入:输入流映射到SQL动态输入表和动态表。这里的动态其实是对比批处理的静态(有界的)。静态表:应用于批量数据,静态表可以理解为不随时间实时变化。一般以一天或一小时为粒度生成一个新的分区。动态表:动态表随时间实时变化。将SQL系统中表的概念应用到Flink中是核心点。看一个具体的例子,下图显示了点击事件流(左)如何转换为动态表(右)。当数据源产生更多的点击事件记录时,映射的动态表也会不断增长。这就是动态表的概念:DynamicTable4。SQL流处理计算:实时处理的底层技术——SQL连续查询连续查询。一些高级关系数据库系统提供了一种称为物化视图的功能。物化视图实际上是一个SQL查询,就像一个常规的虚拟视图VIEW一样。但与虚拟视图不同的是,物化视图会缓存查询的结果,因此在请求访问视图时,不需要重新计算查询,直接获取物化视图的结果即可。小伙伴们可以认为物化视图其实就是把结果缓存下来。举个例子:在批处理中,如果我们以Hive的日级物化视图为例,它实际上每天都在等待数据源准备好,调度物化视图的SQL执行,产生新的结果提供服务。那么可以认为一条代表输入、处理、输出的SQL就是一个构建物化视图的过程。映射到我们的流任务中,输入、处理逻辑、输出的流程集合也是物化视图的一个概念。与批处理相比,在流处理中,我们的数据源表中的数据是连续的。那么整个物化视图从输入、处理、输出的维护过程也必须是实时的。因此,我们需要引入一种实时视图维护(EagerViewMaintenance)技术来做:物化视图的数据源表一旦更新,立即更新视图的结果,从而保证输出结果也是最新的。这种EagerViewMaintenance技术称为连续查询。注意:持续查询(ContinuousQuery)不断地消费动态输入表的数据,不断更新动态结果表的数据。连续查询的输出=在输入表上以批处理模式执行相同查询的结果。相同的SQL对应相同的输入数据。虽然执行方式不同,但是流处理和批处理的结果永远是一样的。5.SQL流处理的实际应用:动态表&连续查询技术的两个实际案例总结前两节,动态表&连续查询两种技术在一个流SQL中的执行过程一共包含三个步骤,如图如下图和总结所示:查询第一步:将数据输入流转换为SQL中的动态输入表。这里的转换实际上是指将输入流映射(绑定)成一个动态输入表。上图虽然是分开画的,但是可以理解为一个东西。第二步:对动态输入表执行连续查询,生成新的动态结果表。第三步:将生成的动态结果表转换回数据输出流。下面我们实际介绍一个案例,看看它是如何工作的。以上面介绍的点击事件流为例,点击事件流数据的字段如下:[user:VARCHAR,//用户名cTime:TIMESTAMP,//URL被访问的时间url:VARCHAR//URL被访问由用户]第一步是将输入数据流映射到动态输入表。以下图为例,我们将点击事件流(图中左侧)转换为动态表(图中右侧)。当点击数据源源不断的到来时,动态表中的数据也会不断增加。DynamicTable第二步是对点击事件流映射的动态输入表执行连续查询(ContinuousQuery),生成新的动态输出表。下面介绍两个查询案例:第一个查询:一个简单的GROUP-BYCOUNT聚合查询,写过SQL的都不会陌生,这应该是最基本也是最常用的数据分类分组的方法了。一个常见的groupby聚合情况如下图所示。time在这种情况下,clicks表(点击事件流)根据clicks表中的user字段进行分组,统计每个用户访问url的次数。下图展示了当clicks输入表有新数据时(即更新表时)ContinuousQuery的计算逻辑。groupagg查询开始时,clicks表(左侧)为空。当第一行数据插入clicks表后,连续查询(ContinuousQuery)开始计算结果数据。输入数据源表中第一行数据[Mary,./home]后,计算结果[Mary,1]插入到结果表中。当第二行[Bob,./cart]被插入到clicks表中时,ContinuousQuery会计算结果[Bob,1]并将其插入到结果表中。当第三行输出[Mary,./prod?id=1]时,会计算[Mary,2](用户是Mary的数据来了两次,所以是2),更新(update)结果表,[Mary,1]更新为[Mary,2]。最后,当第四行数据添加到clicks表时,查询将第三行[Liz,1]插入到结果表中。注意上面特别标注的字体,可以看到对于连续查询的结果输出数据有两种方式:插入(insert)结果表更新(update)结果表大家对插入(insert)比较了解)结果表,因为离线数据只有插入的概念。但是更新结果表意味着离线处理没有概念。这是连续查询中的一个重要概念。稍后会介绍。接下来介绍第二条查询语句。第二种查询和第一种类似,但是除了groupby中的user字段外,groupby中还有一个tumble,意思是打开了一个滚动窗口(滚动窗口的作用后面会有说明)后面会详细介绍),然后计算url的个数。groupbyuser是按类别(横向)对数据进行分组,groupbytumblerollingwindow是按时间粒度(纵向)对数据进行分组。如下所示。时间的图形解释很容易理解。两者都是分组数据,一个是按类别分组,一个是按时间分组。和以前一样,输入表clicks显示在左侧。查询每小时连续计算结果并更新结果表。clicks表有三列,user、cTime、url。其中,cTime表示数据的时间戳,用于按照时间粒度对数据进行分组。tumblewindow我们的tumblingwindow的步长是1小时,也就是时间粒度上面的分组是1小时。其中,有4条时间戳在12:00:00-12:59:59之间的数据。13:00:00-13:59:59有3条数据。14:00:00-14:59:59之间有4条数据。12:00:00-12:59:59数据输入后,1小时的窗口,连续查询(ContinuousQuery)计算结果如右图所示,将[Mary,3],[Bob,1]插入(插入)结果表。13:00:00-13:59:59数据输入后,1小时窗口,连续查询(ContinuousQuery)计算结果如右图所示,插入[Bob,1],[Liz,2]进入(插入)结果表。14:00:00-14:59:59数据输入后,1小时窗口,连续查询(ContinuousQuery)计算结果如右图,[Mary,1],[Bob,2],[Liz,1]插入结果表。而这个查询只有插入结果表的行为。6.两种类型的SQL顺序查询:更新查询和追加查询虽然上一节中的两种查询看起来非常相似(都是按计数聚合计算分组),但它们在一个重要方面有所不同:第一个查询(按用户分组),即(Update)查询:会更新之前输出的结果,即结果表流数据包含INSERT和UPDATE数据。萌友可以理解为groupbyuser语句,输入源的数据一直都在,以后可能还存在同一个用户的数据,所以可以认为是这条sql每次的输出结果它们都是中间结果。当同一用户的下一条数据到达时,需要用新的结果来UPDATE最后输出的中间结果(旧结果)。所以这就是UPDATE查询的由来(其中INSERT是第一条数据到达的时候,没有之前的中间结果,所以是INSERT)。第二种查询(groupbyuser,tumble(xxx)),即(Append)查询:只追加到结果表,即结果表流数据只包含INSERT数据。萌友可以理解,虽然groupbyuser的上游,tumble(xxx)也是源源不断的数据,但是这个查询本质上是一个时间的划分,而且时间越来越大。当前滚动窗口结束后,后续数据的时间会比本次滚动窗口的结束时间长,都属于后续窗口,当前滚动窗口的结果数据不会再发生变化,所以这个query只有INSERT数据,即Append查询。以上就是FlinkSQL持续查询处理机制上的两类查询方式。我们可以发现,连续查询的处理机制不同,输出到结果表的结果数据也不同。针对以上两种结果表的更新方式,FlinkSQL为了兼容,提出了changelogtable的概念。changelog表的概念其实和MySQL的binlog是一样的。它会包含INSERT、UPDATE、DELETE三种数据,通过对这三种数据的处理来描述动态表变化的实时处理技术:changelog表:第一次查询的输出表,输出结果数据不仅会追加,还会更新changeloginsert-only表:即第二次查询的输出表,输出结果数据只会追加,不会发生update。7、SQL流处理的输出:将动态输出表转换为输出数据。可以看到我们的标题都是它遵循一条SQL的生命周期。本节从输入流映射到SQL动态输入表,底层技术实时处理——SQL连续查询到SQL动态输出表到输出数据的转换。这一切都是合乎逻辑的。上面我们提到ContinuousQuery的输出结果表是一个changelog。可以像普通数据库表一样通过INSERT、UPDATE、DELETE不断修改。它可以是单行的、不断更新的变更日志表,也可以是没有UPDATE和DELETE修改的仅插入变更日志表,或者介于两者之间。在将动态表转换为流或将其写入外部系统时,需要对这些不同状态的数据进行编码。Flink的TableAPI和SQLAPI支持三种方式对动态表的变化进行编码:Append-onlystream:输出结果只是INSERT操作的数据。撤回流:撤回流包含两种类型的消息:添加消息和撤回消息。它将INSERT操作编码为添加消息,将DELETE操作编码为收回消息,将UPDATE操作编码为用于更新先前行的收回消息以及用于更新(新)行的添加消息,从而将动态表转换为收回流。Retract流写入输出结果表的数据如下图所示。有两种:-和+,分别是-表示撤回旧数据,+表示输出最新数据。这两种类型的数据最终都会写入输出数据引擎。如果有下游任务消费这个流,需要注意正确处理-和+数据,防止重复或者数据计算出错。retractUpsert流:Upsert流包含两种类型的消息:更新插入消息和删除消息。转换为upsert流的动态表需要唯一键(唯一键可以由多个字段组成)。它将INSERT和UPDATE操作编码为更新插入消息,将DELETE操作编码为删除消息。Upsert流写入输出结果表的数据如下图所示。每次输出的结果都是当前每个用户最新的结果数据,Retract中不会有撤回数据。如果有另一个下游任务消费这个流,消费流的操作者需要知道唯一键(用户),以便根据唯一键(用户)正确获取每个用户的最新状态。它与retract流的主要区别在于UPDATE操作使用单个消息进行编码,因此效率更高。下图显示了将动态表转换为更新插入流的过程。upsert8.补充知识:SQL和关系代数的朋友会问,什么是关系代数?实际上,关系代数是对数据集(即表)的一系列操作(即查询语句)。常见的关系代数有:RelationalAlgebra那么SQL和关系代数是什么关系呢?SQL是一个面向用户的接口,可以表达关系代数:也就是用户可以用SQL来表达关系代数的处理逻辑,也就是我们可以用SQL来对表执行我们的业务逻辑操作(关系代数操作)(数据集)。
