1)Flink对批处理和流处理,提供了统一的上层API;
2)Table API是一套内嵌在Java和Scala语言中的查询API,它允许以非常直观的方式组合来自一些关系运算符的查询;
3)Flink的SQL支持基于实现了SQL标准的Apache Calcite;
1、基本程序结构
1)Table API和SQL的程序结构,与流式处理的程序结构十分类似
2、创建TableEnvironment
1)创建表的执行环境,需要将flink流处理的执行环境传入;
2)TableEnvironment是flink中集成Table API和SQL的核心概念,所有对表的操作都基于TableEnvironment
—— 注册Catalog
—— 在Catalog中注册表
—— 执行SQL查询
—— 注册用户自定义函数(UDF)
3、配置TableEnvironment
1)配置老版本planner的流式查询
2)配置老版本planner的批式查询
3)配置blink planner的流式查询
4)配置blink planner的批式查询
1)TableEnvironment可以注册目录Catalog,并可以基于Catalog注册表;
2)表(Table)是由一个“标识符”(identifier)来指定的,由3部分组成:Catalog名、数据库(database)名和对象名;
3)表可以是常规的,也可以是虚拟的(视图,View);
4)常规表(Table)一般可以用来描述外部数据,比如文件、数据库表或消息队列的数据,也可以直接从DataStream转换而来;
5)视图(View)可以从现有的表中创建,通常是Table API或者SQL查询的一个结果表;
1、创建表
1)TableEnvironment可以调用.connect()方法,连接外部系统,并调用.createTemporaryTable()方法,在Catalog中注册表;
2)可以创建Table来描述文件数据,它可以从文件中读取,或者将数据写入文件
2、表的查询——Table API
1)Table API是集成在Scala和Java语言内的查询API;
2)Table API基于代表“表”的Table类,并提供一整套操作处理的方法API;这些方法会返回一个新的Table对象,表示对输入表应用转换操作的结果;
3)有些关系型转换操作,可以由多个方法调用组成,构成链式调用结构;
4)Flink的SQL集成,基于实现了SQL标准的Apache Calcite;
5)在Flink中,用常规字符串来定义SQL查询语句;
6)SQL查询的结果,也是一个新的Table;
3、将DataStream转换成表
1)对于一个DataStream,可以直接转换成Table,进而方便地调用Table API做转换操作;
2)默认转换后的Table schema和DataStream中的字段定义一一对应,也可以单独指定出来;
4、数据类型与Schema的对应
1)DataStream中的数据类型,与表的Schema之间的对应关系,可以有两种:基于字段名称,或者基于字段位置;
2)基于名称(name-based)
3)基于位置(position-based)
5、创建临时视图(Temporary View)
1)基于DataStream创建临时视图
2)基于Table创建临时视图
6、输出表
1)表的输出,是通过将数据写入TableSink来实现的;
2)TableSink是一个通用接口,可以支持不同的文件格式、存储数据库和消息队列;
3)输出表最直接的方法,就是通过Table.insertInto()方法将一个Table写入注册过的TableSink中
7、输出到文件
8、更新模式
1)对于流式查询,需要声明如何在表和外部连接器之间执行转换;
2)与外部系统交换的消息类型,由更新模式(Update Mode)指定
》》追加(Append)模式
—— 表只做插入操作,和外部连接器只交换插入(Insert)消息;
》》撤回(Retract)模式
—— 表和外部连接器交换添加(Add)和撤回(Retract)消息;
—— 插入操作(Insert)编码为Add消息;删除(Delete)编码为Retract消息;更新(Update)编码为上一条的Retract和下一条的Add消息;
》》更新插入(Upsert)模式
—— 更新和插入都被编码为Upsert消息;删除编码为Delete消息;
9、输出到Kafka
1)可以创建Table来描述kafka中的数据,作为输入或输出的TableSink
10、输出到ES
1)可以创建Table来描述ES中的数据,作为输出的TableSink
11、输出到MySQL
1)可以创建Table来描述MySQL中的数据,作为输入和输出
12、将Table转换成DataStream
1)表可以转换为DataStream或DataSet,这样自定义流处理或批处理程序就可以继续在Table API或SQL查询的结果上运行了;
2)将表转换为DataStream或DataSet时,需要指定生成的数据类型,即要将表的每一行转换成的数据类型;
3)表作为流式查询的结果,是动态更新的;
4)转换有两种转换模式:追加(Append)模式和撤回(Retract)模式;
》》追加模式(Append Mode)
—— 用于表只会被插入(Insert)操作更改的场景
》》撤回模式(Retract Mode)
—— 用于任何场景,有些类似于更新模式中Retract模式,它只有Insert和Delete两类操作;
—— 得到的数据会增加一个Boolean类型的标识位(返回的第一个字段,用它来标识到底是新增的数据(Insert)),还是被删除的数据(Delete)。
13、查看执行计划
1)Table API提供了一种机制来解释计算表的逻辑和优化查询计划;
2)查看执行计划,可以通过TableEnvironment.explain(table)方法或TableEnvironment.explain()方法完成,返回一个字符串,描述三个计划:
》》优化的逻辑查询计划;
》》优化后的逻辑查询计划;
》》实际执行计划;
14、流处理和关系代数的区别
|
关系代数(表)/SQL |
流处理 |
处理的数据对象 |
字段元组的有界集合 |
字段元组的无限序列 |
查询(Query) 对数据的访问 |
可以访问到完整的数据输入 |
无法访问所有数据, 必须持续“等待”流式输入 |
查询终止条件 |
生成固定大小的结果集后终止 |
永不停止,根据持续收到的数据不断更新查询结果 |
15、动态表(Dynamic Tables)
1)动态表是Flink对流数据的Table API和SQL支持的核心概念;
2)与表示批处理数据的静态表不同,动态表是随时间变化的;
》》持续查询(Continuous Query)
(1)动态表可以像静态的批处理表一样进行查询,查询一个动态表会产生持续查询(Continuous Query);
(2)连续查询永远不会终止,并会生成另一个动态表;
(3)查询会不断更新其动态结果表,以反映其动态输入表上的更改;
16、动态表和持续查询
》》流式表查询的处理过程:
(1)流被转换为动态表;
(2)对动态表计算连续查询,生成新的动态表;
(3)生成的动态表被转换回流;
17、将流转换成动态表
1)为了处理带有关系查询的流,必须先将其转换为表;
2)从概念上将,流的每个数据记录,都被解释为结果表的插入(Insert)修改操作;
18、持续查询
1)持续查询会在动态表上做计算处理,并作为结果生成新的动态表;
19、将动态表转换成DataStream
1)与常规的数据库表一样,动态表可以通过插入(Insert)、更新(Update)和删除(Delete)更改,进行持续的修改;
2)将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码;
》》仅追加(Append-only)流
—— 仅通过插入(Insert)更改来修改的动态表,可以直接转换为仅追加流;
》》撤回(Retract)流
—— 撤回流流是包含两类消息的流:添加(Add)消息和撤回(Retract)消息;
》》Upsert(更新插入)流
—— Upsert流也包含两种类型的消息:Upsert消息和删除(Delete)消息;
20、时间特性(Time Attributes)
1)基于时间的操作(比如Table API和SQL中窗口操作),需要定义相关的时间语义和时间数据来源的信息;
2)Table可以提供一个逻辑上的时间字段,用于在表处理程序中,指示时间和访问相应的时间戳;
3)时间属性,可以是每个表schema的一部分。一旦定义了时间属性,它就可以作为一个字段引用,并且可以在基于时间的操作中使用;
4)时间属性的行为类似于常规时间戳,可以访问,并且进行计算;
21、定义处理时间(Processing Time)
1)处理时间语义下,允许表处理程序根据机器的本地时间生成结果。它是时间的最简单概念,它既不需要提取时间戳,也不需要生成Watermark;
》》由DataStream转换成表时指定
(1)在定义Schema期间,可以使用.proctime,指定字段名定义处理时间字段;
(2)这个proctime属性只能通过附加逻辑字段,来扩展物理schema。因此,只能在schema定义的末尾定义它
》》定义Table Schema时指定
》》在创建表的DDL中定义
2)事件时间语义,允许表处理程序根据每个记录中包含的时间生成结果。这样即使在有乱序事件或者延迟事件时,也可以获得正确的结果。
3)为了处理无序事件,并区分流中的准时和迟到事件;Flink需要从事件数据中,提取时间戳,并用来推进事件时间的进展;
4)定义事件时间,同样有三种方法:
》由DataStream转换成表时指定
》定义Table Schema时指定
》在创建表的DDL中定义
》》由DataStream转换成表时指定
(1)在DataStream转换成Table,使用.rowtime可以定义事件时间属性
(2)定义Table Schema时指定
(3)在创建表的DDL中定义
22、窗口
1)时间定义,要配合窗口操作才能发挥作用;
2)在Table API和SQL中,主要有两种窗口
》》Group Windows(分组窗口)
—— 根据时间或行计数间隔,将行聚合到有限的组(Group)中,并对每个组的数据执行一次聚合函数;
》》Over Windows
—— 针对每个输入行,计算相邻行范围内的聚合;
22.1 Group Windows
(1)Group WIndows是使用window(w:GroupWindow)子句定义的,并且必须由as子句指定一个别名。
(2)为了按窗口对表进行分组,窗口的别名必须在group by子句中,像常规的分组字段一样引用
(3)Table API提供了一组具有特定语义的预定义Window类,这些类会被转换为底层DataStream或DataSet的窗口操作;
22.2 滚动窗口(Tumbling windows)
(1)滚动窗口要用Tumble类来定义
22.3 滑动窗口(Sliding windows)
(1)滑动窗口要用slide类来定义
22.4 会话窗口(Session windows)
(1)会话窗口要用Session类来定义
22.5 Over Windows
(1)Over window聚合是标准SQL中已有的(over子句),可以在查询的SELECT子句中定义;
(2)Over window聚合,会针对每个输入行,计算相邻行范围内的聚合;
(3)Over windows使用window(w:overwindows*)子句定义,并在select()方法中通过别名来引用
4)Table API提供了Over类,来配置Over窗口的属性;
22.6 无界Over Windows
1)可以在事件时间或处理时间,以及指定为时间间隔,或行计数的范围内,定义Over windows
2)无界的over window是使用常量指定的
22.7 有界Over Windows
1)有界的over window是用间隔的大小指定的
23、SQL中的Group Windows
1)Group Windows定义在SQL查询的Group By子句中
》》TUMBLE(time_attr,interval)
(1)定义一个滚动窗口,第一个参数是时间字段,第二个参数是窗口长度;
》》HOP(time_attr,interval)
(1)定义一个滑动窗口,第一个参数是时间字段,第二个参数是窗口滑动步长,第三个是窗口长度;
》》SESSION(time_attr,interval)
(1)定义一个会话窗口,第一个参数是时间字段,第二个参数是窗口间隔;
24、SQL中的Over Windows
1)用Over做窗口聚合时,所有聚合必须在同一窗口上定义,也就是说必须是相同的分区、排序和范围;
2)目前仅支持在当前行范围之前的窗口;
3)ORDER BY必须在单一的时间属性上指定
25、函数(Functions)
1)Flink Table API 和SQL为用户提供了一组用于数据转换的内置函数;
2)SQL中支持的很多函数,Table API和SQL都已经做了实现;
26、用户自定义函数(UDF)
1)用户定义函数(User-defined Functions,UDF)是一个重要的特性,它们显著地扩展了查询的表达能力;
2)在大多数情况下,用户定义的函数必须先注册,然后才能在查询中使用;
3)函数通过调用registerFunction()方法在TableEnvironment中注册。当用户定义的函数被注册时,它被插入到TableEnvironment的函数目录中,这样Table API或SQL解析器就可以识别并正确地解释它;
27、标量函数(Scalar Functions)
1)用户定义的标量函数,可以将0、1或多个标量值,映射到新的标量值;
2)为了定义标量函数,必须在org.apache.flink.table.functions中扩展基类Scalar Function,并实现(一个或多个)求值(eval)方法;
3)标量函数的行为由求值方法决定,求值方法必须公开声明并命名为eval
28、表函数(Table Functions)
1)用户定义的表函数,也可以将0、1或多个标量值作为输入参数;与标量函数不同的是,它可以返回任意数量的行行为输出,而不是单个值;
2)为了定义一个表函数,必须扩展org.apache.flink.table.functions中的基类TableFunction并实现(一个或多个)求值方法;
3)表函数的行为由其求值方法决定,求值方法必须是public的,并命名为eval:
29、聚合函数(Aggregate Functions)
1)用户自定义聚合函数(User-Defined Aggregate Functions,UDAGGs)可以把一个表中的数据,聚合成一个标量值;
2)用户定义的聚合函数,是通过继承AggregateFunction抽象类实现的
3)AggregationFunction要求必须实现的方法:
—— createAccumulator()
—— accumulate()
—— getValue()
4)AggregateFunction的工作原理如下:
—— 首先,它需要一个累加器(Accumulator),用来保存聚合中间结果的数据结构;可以通过调用createAccumulator()方法创建空累加器;
—— 随后,对每个输入行调用函数的accumulate()方法来更新累加器;
—— 处理完所有行后,将调用函数的getValue()方法来计算并返回最终结果;
30、表聚合函数(Table Aggregate Functions)
1)用户定义的表聚合函数(User-defined Table Aggregate Functions,UDTAGGs),可以把一个表中数据,聚合为具有多行和多列的结果表;
2)用户定义表聚合函数,是通过集成TableAggregateFunction抽象类来实现的;
3)AggregationFunction要求必须实现的方法:
—— createAccumulator()
—— accumulate()
—— emitValue()
4)TableAggregateFunction的工作原理如下:
—— 首先,它同样需要一个累加器(Accumulator),它是保存聚合中间结果的数据结构。通过调用createAccumulator()方法可以创建空累加器;
—— 随后,对每个输入行调用函数的accumulate()方法来更新累加器;
—— 处理完所有行后,将调用函数的emitValue()方法来计算并返回最终结果;