FlinkSQL基本API

  Table API 和 SQL 可以看作联结在一起的一套 API,这套 API 的核心概念就是"表"(Table)。在我们的程序中,输入数据可以定义成一张表;然后对这张表进行查询,就可以得到新的表,这相当于就是流数据的转换操作;最后还可以定义一张用于输出的表,负责将处理结果写入到外部系统。

  整体处理流程可以分为读取数据源(Source)、转换(Transform)、输出数据(Sink)三部分,只需要将用于输入和输出的表定义出来,然后进行转换查询就可以。

// 创建表环境
TableEnvironment tableEnv = ...;
// 创建输入表,连接外部系统读取数据
tableEnv.executeSql("CREATE TEMPORARY TABLE inputTable ... WITH ( 'connector' = ... )");
// 注册一个表,连接到外部系统,用于输出
tableEnv.executeSql("CREATE TEMPORARY TABLE outputTable ... WITH ( 'connector' = ... )");
// 执行 SQL 对表进行查询转换,得到一个新的表
Table table1 = tableEnv.sqlQuery("SELECT ... FROM inputTable... ");
// 使用 Table API 对表进行查询转换,得到一个新的表
Table table2 = tableEnv.from("inputTable").select(...);
// 将得到的结果写入输出表
TableResult tableResult = table1.executeInsert("outputTable");

  通过执行 DDL 来直接创建一个表。这里执行的 CREATE 语句中用 WITH 指定了外部系统的连接器,于是就可以连接外部系统读取数据了。这其实是更加一般化的程序架构,因为这样我们就可以完全抛开DataStream API,直接用 SQL 语句实现全部的流处理过程。

创建表环境

   使用 TableAPI 和 SQL 需要一个特别的运行时环境,这就是"表环境"(TableEnvironment)。主要负责:

  1. 注册Catalog 和表;
  2. 执行 SQL 查询;
  3. 注册用户自定义函数(UDF)
  4. DataStream 和表之间的转换

    Catalog 就是"目录"主要用来管理所有数据库(database)和表(table)的元数据(metadata),通过 Catalog 可以方便地对数据库和表进行查询的管理,可以认为我们所定义的表都会"挂靠"在某个目录下,这样就可以快速检索。在表环境中可以由用户自定义Catalog,并在其中注册表和自定义函数(UDF)。默认的 Catalog就叫作 default_catalog。

   每个表和 SQL 的执行,都必须绑定在一个表环境(TableEnvironment)中。TableEnvironment是 Table API 中提供的基本接口类,通过调用静态的 create()方法来创建一个表环境实例。方法需要传入一个环境的配置参数EnvironmentSettings,可以指定当前表环境的执行模式和计划器(planner)。执行模式有批处理和流处理两种选择,默认是流处理模式;计划器默认使用 blink planner。

import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;
EnvironmentSettings settings = EnvironmentSettings
.newInstance()
.inStreamingMode()  // 使用流处理模式
.build();
TableEnvironment tableEnv = TableEnvironment.create(settings);

  另一种更加简单的方式来创建表环境:

import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; 
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); 
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        " 流式表环境 "( StreamTableEnvironment ), 它是继承自TableEnvironment 的子接口。调用它的 create() 方法, 只需要直接将当前的流执行环境(StreamExecutionEnvironment)传入,就可以创建出对应的流式表环境

三、创建表

     为了方便地查询表,表环境中会维护一个目录(Catalog)和表的对应关系。所以表都是通过Catalog 来进行注册创建的。表在环境中有一个唯一的 ID,由三部分组成:目录(catalog) 名,数据库(database)名,以及表名。在默认情况下,目录名为 default_catalog,数据库名为default_database。所以如果我们直接创建一个叫作 MyTable 的表,它的 ID 就是:

default_catalog.default_database.MyTable

  创建表的方式,有通过连接器(connector)和虚拟表(virtual tables)两种

  1. 连接器表(Connector Tables)

  通过连接器(connector)连接到一个外部系统,然后定义出 对应的表结构。例如可以连接到 Kafka 或者文件系统,将存储在这些外部系统的数据以"表" 的形式定义出来,对表的读写就可以通过连接器转换成对外部系统的读写。当在表 环境中读取这张表,连接器就会从外部系统读取数据并进行转换;而当向这张表写入数据, 连接器就会将数据输出(Sink)到外部系统中。

   可以调用表环境的 executeSql()方法,可以传入一个 DDL 作为参数执行SQL 操作。这里我们传入一个CREATE 语句进行表的创建,并通过 WITH 关键字指定连接到外部系统的连接器:

tableEnv.executeSql("CREATE [TEMPORARY] TABLE MyTable ... WITH ( 'connector' = ... )");

  如果希望使用自定义的目录名和库名,可以在环境中进行设置:

tEnv.useCatalog("custom_catalog");
tEnv.useDatabase("custom_database");

2. 虚拟表(Virtual Tables)

  环境中注册之后,我们就可以在 SQL 中直接使用这张表进行查询转换

Table newTable = tableEnv.sqlQuery("SELECT ... FROM MyTable... ");

  调用了表环境的 sqlQuery()方法,直接传入一条 SQL 语句作为参数执行查询,得到的结果是一个 Table 对象。Table 是 Table API 中提供的核心接口类,就代表了一个 Java 中定义的表实例。

  得到的 newTable 是一个中间转换结果,如果之后又希望直接使用这个表执行 SQL,又该怎么做呢?由于 newTable 是一个 Table 对象,并没有在表环境中注册;所以我们还需要将这个 中 间 结 果 表 注 册 到 环 境 中 , 才 能 在 SQL 中 使 用 :

tableEnv.createTemporaryView("NewTable", newTable);

  这里的注册其实是创建了一个"虚拟表"(Virtual Table)。这个概念与 SQL 语法中的视图(View)非常类似,所以调用的方法也叫作创建"虚拟视图"(createTemporaryView)。视图之所以是"虚拟"的,是因为我们并不会直接保存这个表的内容,并没有"实体";只是在用到这张表的时候,会将它对应的查询语句嵌入到 SQL 中。注册为虚拟表之后,我们就又可以在 SQL 中直接使用 NewTable 进行查询转换了。

  读入表–读入内存格式

// 创建表
 tableEnv.executeSql("CREATE TABLE EventTable (" +
         "user_name STRING," +
         "url STRING," +
         "ts BIGINT)" +
         " with (" +
         " 'connector' = 'filesystem'," +
         " 'path' = 'input/clicks.txt'," +
         " 'format' = 'csv'" +
         ")");

  输出表–输出存储格式

// 创建输出表
 tableEnv.executeSql("CREATE TABLE outTable (" +
         "user_name STRING," +
         "url STRING" +
         ") with (" +
         " 'connector' = 'filesystem'," +
         " 'path' = 'output'," +
         " 'format' = 'csv'" +
         ")");

四、表的查询

  对一个表的查询(Query)操作,就对应着流数据的转换(Transform)处理。

1. 执行 SQL 进行查询

  Flink 基于 Apache Calcite 来提供对SQL 的支持,Calcite 是一个为不同的计算平台提供标准 SQL 查询的底层工具,很多大数据框架比如 Apache Hive、Apache Kylin 中的SQL 支持都是通过集成 Calcite 来实现的。

  只要调用表环境的 sqlQuery()方法,传入一个字符串形式的 SQL 查询语句就可以了。执行得到的结果,是一个 Table 对象。

// 查询用户 Alice 的点击事件,并提取表中前两个字段
Table aliceVisitTable = tableEnv.sqlQuery( "SELECT user, url " + "FROM EventTable " + "WHERE user = 'Alice' " );

  也可以直接将查询的结果写入到已经注册的表中,这需要调用表环境的executeSql()方法来执行DDL,传入的是一个INSERT 语句:

// 将查询结果输出到 OutputTable 中
tableEnv.executeSql ( "INSERT INTO OutputTable " +
"SELECT user, url " + "FROM EventTable " + "WHERE user = 'Alice' ");
2. 调用 Table API 进行查询

  嵌入在 Java 和 Scala 语言内的查询 API,核心就是 Table 接口类,通过一步步链式调用 Table 的方法,就可以定义出所有的查询转换操作。每一步方法调用的返回结果,都是一个Table。

  由于Table API 是基于Table 的Java 实例进行调用的,因此我们首先要得到表的Java 对象。基于环境中已注册的表,可以通过表环境的 from()方法非常容易地得到一个Table 对象:

Table eventTable = tableEnv.from("EventTable");

  传入的参数就是注册好的表名。注意这里 eventTable 是一个Table 对象,而EventTable 是在环境中注册的表名。得到 Table 对象之后,就可以调用 API 进行各种转换操作了,得到的是一个新的Table 对象:

Table maryClickTable = eventTable
.where($("user").isEqual("Alice"))
.select($("url"), $("user"));

  "$"符号用来指定表中的一个字段。上面的代码和直接执行 SQL 是等效的。 Table API 是嵌入编程语言中的DSL,SQL 中的很多特性和功能必须要有对应的实现才可以使用,因此跟直接写 SQL 比起来肯定就要麻烦一些。

五、输出表

  对应着流处理中的读取数据源(Source)和转换(Transform);而最后一个步骤 Sink,也就是将结果数据输出到外部系统,就对应着表的输出操作。在代码上,输出一张表最直接的方法,就是调用 Table 的方法executeInsert()方法将一个Table 写入到注册过的表中,方法传入的参数就是注册的表名。

// 将结果表写入已注册的输出表中
result.executeInsert("OutputTable");

  在底层,表的输出是通过将数据写入到TableSink 来实现的。TableSink 是Table API 中提供的一个向外部系统写入数据的通用接口,可以支持不同的文件格式(比如 CSV、Parquet)、存储数据库(比如 JDBC、HBase、Elasticsearch)和消息队列(比如 Kafka)。

  

posted on 2023-06-03 12:03  溪水静幽  阅读(186)  评论(0)    收藏  举报