Flink stream和table SQL API下多流,多分枝,流复用,表复用支持的讨论

Flink stream API和table SQL API下多流,多分枝,流复用 支持的讨论

一、stream API

stream API是支持将一个流(DataStream)使用多次的。很简单例子。可以将一个流打印多次。
需要注意:stream API中是可以使用Table API的。
基本流程是:
StreamSource --> Stream API --> StreamToTable --> Table API 使用各种转化 --> TableToStream --> StreamSink
即只要开头 和 结尾不使用Table API就可以了。如source不能使用 tableEnv.from(tablePath) 或者 tableEnv.sqlQuery( ... )
而sink 不能使用 sourceTblObj.executeInsert(distintionPath);tenv.executeSql( ... )
而在中间转换过程中是可以使用 tableEnv.createTemporaryView(表路径, 表对象) 将从而使用 Table new tblObj=tableEnv.sqlQuery( "SQL 语句" ) 进行相关的转换。因此在中间转换过程中可以实现SQL 和 DSL风格语句无缝转换。flink 内部会自动将SQL翻译为DSL的执行语句的。

二、Table API

2.1 结论:

table API仅仅支持一个表 输入和 输出。并不支持。一个表简单的多次使用。如果需要多次使用一个表需要执行tableEnv.createStatementSet() API才可以。另外经过验证,将一个Table转换为DataStream 再转换会Table一样也不能实现多次使用一个表,也就是没法实现Table对象的复制。

2.2 官网原文解释

官网原文(1)flink 1.13
当一个表被多次使用的时候,会将查询的过程内联(可能像面向对象的内联对象),结果就是追溯流的来源多次执行。
比如一个flink sql jdbc表被多次查询,实际就是向数据库 申请了多次查询。或者 flink cdc stream API 将stream转化为table后,多次使用table对象,会造成flink cdc 向数据库多次申请获取日志,而不是branch(复用)已有的Stream。

Note: Table objects are similar to VIEW’s from relational database systems, i.e., the query that defines the Table is not optimized but will be inlined when another query references the registered Table. If multiple queries reference the same registered Table, it will be inlined for each referencing query and executed multiple times, i.e., the result of the registered Table will not be shared.

官网原文(2) flink 1.13

注意这里说的branch 就是将一个流多次使用,即多分支任务的作用。
可以看到对于Table API 当不使用 statement set 的时候,就只支持 a single source-to-sink pipeline

DataStream API

The DataStream API’s StreamExecutionEnvironment acts as a builder pattern to construct a complex pipeline. The pipeline possibly splits into multiple branches that might or might not end with a sink.
At least one sink must be defined. Otherwise, the following exception is thrown:

java.lang.IllegalStateException: No operators defined in streaming topology. Cannot execute.
StreamExecutionEnvironment.execute() submits the entire constructed pipeline and clears the builder afterward. In other words: no sources and sinks are declared anymore, and a new pipeline can be added to the builder. Thus, every DataStream program usually ends with a call to StreamExecutionEnvironment.execute(). Alternatively, DataStream.executeAndCollect() implicitly defines a sink for streaming the results to the local client and only executes the current branch.

Table API
In the Table API, branching pipelines is only supported within a StatementSet where each branch must declare a final sink. Both TableEnvironment and also StreamTableEnvironment do not offer a dedicated general execute() method. Instead, they offer methods for submitting a single source-to-sink pipeline or a statement set:

引用官网的 createStatementSet 案例:

// execute with explicit sink
tableEnv.from("InputTable").executeInsert("OutputTable")

tableEnv.executeSql("INSERT INTO OutputTable SELECT * FROM InputTable")
tableEnv.createStatementSet()
    .addInsert("OutputTable", tableEnv.from("InputTable"))
    .addInsert("OutputTable2", tableEnv.from("InputTable"))
    .execute()
tableEnv.createStatementSet()
    .addInsertSql("INSERT INTO OutputTable SELECT * FROM InputTable")
    .addInsertSql("INSERT INTO OutputTable2 SELECT * FROM InputTable")
    .execute()
// execute with implicit local sink
tableEnv.from("InputTable").execute().print()
tableEnv.executeSql("SELECT * FROM InputTable").print()
2.2 能不能绕开

结论:不能的。

是否可以利用stream api 先把流复制一下,再把复制后的流转化为table呢?答案:不行!!!
如下流程也是行不通的:
在这里插入图片描述
最终不管是 sourceTblObj.executeInsert(distintionPath);tenv.executeSql( ... ) 的insert 都是不可以的。

需要注意:Stream 的 exectue 和 Table API的 execute 方法(execute 是指类似 execute 的方法) 是不会相互触发的。如下的流都是不会执行的。
在这里插入图片描述
Table API 都执行了 execute 如果代码最后没有添加 streamEnv.execute(jobName) 的话,都是不会执行的。因为没有完整的流产生。

再举个例子:
比如向通过flink CDC stream API将在日志中不同表读取出来再通过tableApi Sink到其他库是行不通的。
如下表的流程是走不通的。
在这里插入图片描述
这样会导致日志被多次读取。有几张表和table 的exectue 就会读取几次日志。
如下图,日志会被读取了多次。
在这里插入图片描述
比如两个insert debezium会打印如下:

九月 13, 2023 5:24:08 下午 com.github.shyiko.mysql.binlog.BinaryLogClient connect
信息: Connected to 10.80.21.156:3306 at mysql-bin.000008/77061057 (sid:5917, cid:11536)
九月 13, 2023 5:24:08 下午 com.github.shyiko.mysql.binlog.BinaryLogClient connect
信息: Connected to 10.80.21.156:3306 at mysql-bin.000008/77061057 (sid:5715, cid:11535)

...  for job 7edab4e5af3e3704553e83b691899958 ...
...  for job 11709cd2cbef89294f10495e624a5531  ...
...  for job f080b028795a2e4860fce91c6dd4836b  ...

其中sid是指mysql 的serverId,而cid是指连接mysql的connectionId 。连续两次打印的sid 和 cid都不同。
说明debezium伪装成两个slaver去连接了master mysql服务器,分别读取日志。mysql要处理两个请求。100张表就意味着一百个连接。没有起到分流的作用。

而日中中 for job ID 发现 会有三个不同的ID,说明启动了三个Job,观察checkpoint文件夹发现会有三个。
在这里插入图片描述
三个Job分别是:
(1)stream API (由 streamEnv.execute() 触发)
(2)stream API --> Table API tbl1 sink (由 tbl1.executeInsert()tableEnv.executeSql("insert into tbl1 ... ") 触发)
(3)stream API --> Table API tbl1 sink (由 tbl2.executeInsert()tableEnv.executeSql("insert into tbl2 ... ") 触发)

此时如果如果在stream API中强制指定job-id(PipelineOptionsInternal#PIPELINE_FIXED_JOB_ID 也即 $internal.pipeline.job-id)就会将三个job的checkpoint指定到一个文件夹。会造成checkpoint报错,内容:chk-n 已经存在。

结论:
Flink中如果想多次branch(分支、复用)一个流stream

(1)使用Stream API Source和Sink,不要使用Table的Source或Sink
(2)中间可以将stream转化为table或将table转化为sream 也可以在中间 将 table对象注册到catalog中(createTemporaryView方法)使用DSL或者SQL方式对table进行转化。

引用别人的案例 Flink SQL 一个流 输出 2 次

本以为会两次输出,想达到如下效果。但是实际测试,只有一次输出。

Table table = tableEnvironment.sqlQuery("xxxxxxxxx");
tableEnvironment.createTemporaryView("base_table",table);
tableEnvironment.executeSql("select a,count(1) ct from base_table group by b").print();//第一次使用
tableEnvironment.executeSql("select b,count(1) ct from base_table group by a").print();//第二次使用

其实flink提供了statementset方式,一个表多次复用。其中也是使用stream api的branch方式。
DataStream API Integration
在这里插入图片描述

官网案例:

// execute with explicit sink
tableEnv.from("InputTable").executeInsert("OutputTable")
tableEnv.executeSql("INSERT INTO OutputTable SELECT * FROM InputTable")
tableEnv.createStatementSet()
    .addInsert("OutputTable", tableEnv.from("InputTable"))
    .addInsert("OutputTable2", tableEnv.from("InputTable"))
    .execute()

tableEnv.createStatementSet()
    .addInsertSql("INSERT INTO OutputTable SELECT * FROM InputTable")
    .addInsertSql("INSERT INTO OutputTable2 SELECT * FROM InputTable")
    .execute()

// execute with implicit local sink

tableEnv.from("InputTable").execute().print()
tableEnv.executeSql("SELECT * FROM InputTable").print()

SQL API

同样一张表两次使用需要用到statementset方式。
Execute a set of SQL statements

语法:
注意:
(1)需要在flink sql-client中执行。
(2)实际要一个一个sql的执行。
(3)实际执行的时候不要/没有 { }+

BEGIN STATEMENT SET;
  -- one or more INSERT INTO statements
  { INSERT INTO|OVERWRITE <select_statement>; }+
END;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值