Table API和Flink SQL

概述

  • Flink 对批处理和流处理,提供了统一的上层 API
  • Table API 是一套内嵌在 Java 和 Scala 语言中的查询API,它允许以非常直观的方式组合来自一些关系运算符的查询
  • Flink 的 SQL 支持基于实现了 SQL 标准的 Apache Calcite

在这里插入图片描述

 使用样例

  • 导入pom依赖,1.11.X之后,推荐使用blink版本

    <!-- Table API 和 Flink SQL -->
    <dependency>
      <groupId>org.apache.flink</groupId>
      <artifactId>flink-table-planner-blink_${scala.binary.version}</artifactId>
      <version>${flink.version}</version>
    </dependency>
  • java样例代码

    package apitest.tableapi;
    
    import apitest.beans.SensorReading;
    import org.apache.flink.streaming.api.datastream.DataStream;
    import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
    import org.apache.flink.table.api.Table;
    import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
    import org.apache.flink.types.Row;
    
    /**
     * @author : Ashiamd email: ashiamd@foxmail.com
     * @date : 2021/2/3 5:47 AM
     */
    public class TableTest1_Example {
      public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
    
        // 1. 读取数据
        DataStream<String> inputStream = env.readTextFile("/tmp/Flink_Tutorial/src/main/resources/sensor.txt");
    
        // 2. 转换成POJO
        DataStream<SensorReading> dataStream = inputStream.map(line -> {
          String[] fields = line.split(",");
          return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
        });
    
        // 3. 创建表环境
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
    
        // 4. 基于流创建一张表
        Table dataTable = tableEnv.fromDataStream(dataStream);
    
        // 5. 调用table API进行转换操作
        Table resultTable = dataTable.select("id, temperature")
          .where("id = 'sensor_1'");
    
        // 6. 执行SQL
        tableEnv.createTemporaryView("sensor", dataTable);
        String sql = "select id, temperature from sensor where id = 'sensor_1'";
        Table resultSqlTable = tableEnv.sqlQuery(sql);
    
        tableEnv.toAppendStream(resultTable, Row.class).print("result");
        tableEnv.toAppendStream(resultSqlTable, Row.class).print("sql");
    
        env.execute();
      }
    }
  • Txt文件

    sensor_1,1547718199,35.8
    sensor_6,1547718201,15.4
    sensor_7,1547718202,6.7
    sensor_10,1547718205,38.1
    sensor_1,1547718207,36.3
    sensor_1,1547718209,32.8
    sensor_1,1547718212,37.1

基本程序结构

Table API和SQL的程序结构,与流式处理的程序结构十分类似

StreamTableEnvironment tableEnv = ... // 创建表的执行环境

// 创建一张表,用于读取数据
tableEnv.connect(...).createTemporaryTable("inputTable");

// 注册一张表,用于把计算结果输出
tableEnv.connect(...).createTemporaryTable("outputTable");

// 通过 Table API 查询算子,得到一张结果表
Table result = tableEnv.from("inputTable").select(...);

// 通过SQL查询语句,得到一张结果表
Table sqlResult = tableEnv.sqlQuery("SELECT ... FROM inputTable ...");

// 将结果表写入输出表中
result.insertInto("outputTable");

Table API批处理和流处理

新版本blink,真正把批处理、流处理都以DataStream实现。

创建环境-样例代码

package apitest.tableapi;


import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;
import org.apache.flink.table.api.bridge.java.BatchTableEnvironment;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.table.api.internal.TableEnvironmentImpl;

/**
 * @author : Ashiamd email: ashiamd@foxmail.com
 * @date : 2021/2/3 3:56 PM
 */
public class TableTest2_CommonApi {
  public static void main(String[] args) {
    // 创建执行环境
    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    // 设置并行度为1
    env.setParallelism(1);

    StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

    // 1.1 基于老版本planner的流处理
    EnvironmentSettings oldStreamSettings = EnvironmentSettings.newInstance()
      .useOldPlanner()
      .inStreamingMode()
      .build();
    StreamTableEnvironment oldStreamTableEnv = StreamTableEnvironment.create(env,oldStreamSettings);

    // 1.2 基于老版本planner的批处理
    ExecutionEnvironment batchEnv = ExecutionEnvironment.getExecutionEnvironment();
    BatchTableEnvironment oldBatchTableEnv = BatchTableEnvironment.create(batchEnv);

    // 1.3 基于Blink的流处理
    EnvironmentSettings blinkStreamSettings = EnvironmentSettings.newInstance()
      .useBlinkPlanner()
      .inStreamingMode()
      .build();
    StreamTableEnvironment blinkStreamTableEnv = StreamTableEnvironment.create(env,blinkStreamSettings);

    // 1.4 基于Blink的批处理
    EnvironmentSettings blinkBatchSettings = EnvironmentSettings.newInstance()
      .useBlinkPlanner()
      .inBatchMode()
      .build();
    TableEnvironment blinkBatchTableEnv = TableEnvironment.create(blinkBatchSettings);
  }
}

表(Table)

  • TableEnvironment可以注册目录Catalog,并可以基于Catalog注册表
  • 表(Table)是由一个"标示符"(identifier)来指定的,由3部分组成:Catalog名、数据库(database)名和对象名
  • 表可以是常规的,也可以是虚拟的(视图,View)
  • 常规表(Table)一般可以用来描述外部数据,比如文件、数据库表或消息队列的数据,也可以直接从DataStream转换而来
  • 视图(View)可以从现有的表中创建,通常是table API或者SQL查询的一个结果集

创建表

  • TableEnvironment可以调用connect()方法,连接外部系统,并调用.createTemporaryTable()方法,在Catalog中注册表

    tableEnv
      .connect(...)    //    定义表的数据来源,和外部系统建立连接
      .withFormat(...)    //    定义数据格式化方法
      .withSchema(...)    //    定义表结构
      .createTemporaryTable("MyTable");    //    创建临时表

 创建TableEnvironment

  • 创建表的执行环境,需要将flink流处理的执行环境传入

    StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

  • TableEnvironment是flink中集成Table API和SQL的核心概念,所有对表的操作都基于TableEnvironment

    • 注册Catalog
    • 在Catalog中注册表
    • 执行SQL查询
    • 注册用户自定义函数(UDF)

测试代码

  • pom依赖

    <!-- Table API 和 Flink SQL -->
    <dependency>
      <groupId>org.apache.flink</groupId>
      <artifactId>flink-table-planner-blink_${scala.binary.version}</artifactId>
      <version>${flink.version}</version>
    </dependency>
    
    <!-- csv -->
    <dependency>
      <groupId>org.apache.flink</groupId>
      <artifactId>flink-csv</artifactId>
      <version>${flink.version}</version>
    </dependency>
  • java代码

    package apitest.tableapi;
    
    
    import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
    import org.apache.flink.table.api.DataTypes;
    import org.apache.flink.table.api.Table;
    import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
    import org.apache.flink.table.descriptors.Csv;
    import org.apache.flink.table.descriptors.FileSystem;
    import org.apache.flink.table.descriptors.Schema;
    import org.apache.flink.types.Row;
    
    /**
     * @author : Ashiamd email: ashiamd@foxmail.com
     * @date : 2021/2/3 3:56 PM
     */
    public class TableTest2_CommonApi {
      public static void main(String[] args) throws Exception {
        // 创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 设置并行度为1
        env.setParallelism(1);
    
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
    
        // 2. 表的创建:连接外部系统,读取数据
        // 2.1 读取文件
        String filePath = "/tmp/Flink_Tutorial/src/main/resources/sensor.txt";
    
        tableEnv.connect(new FileSystem().path(filePath)) // 定义到文件系统的连接
          .withFormat(new Csv()) // 定义以csv格式进行数据格式化
          .withSchema(new Schema()
                      .field("id", DataTypes.STRING())
                      .field("timestamp", DataTypes.BIGINT())
                      .field("temp", DataTypes.DOUBLE())
                     ) // 定义表结构
          .createTemporaryTable("inputTable"); // 创建临时表
    
        Table inputTable = tableEnv.from("inputTable");
        inputTable.printSchema();
        tableEnv.toAppendStream(inputTable, Row.class).print();
    
        env.execute();
      }
    }
  • 输入文件

    sensor_1,1547718199,35.8
    sensor_6,1547718201,15.4
    sensor_7,1547718202,6.7
    sensor_10,1547718205,38.1
    sensor_1,1547718207,36.3
    sensor_1,1547718209,32.8
    sensor_1,1547718212,37.1
  • 输出

    root
     |-- id: STRING
     |-- timestamp: BIGINT
     |-- temp: DOUBLE
     
    sensor_1,1547718199,35.8
    sensor_6,1547718201,15.4
    sensor_7,1547718202,6.7
    sensor_10,1547718205,38.1
    sensor_1,1547718207,36.3
    sensor_1,1547718209,32.8
    sensor_1,1547718212,37.1

表的查询

  • Table API是集成在Scala和Java语言内的查询API

  • Table API基于代表"表"的Table类,并提供一整套操作处理的方法API;这些方法会返回一个新的Table对象,表示对输入表应用转换操作的结果

  • 有些关系型转换操作,可以由多个方法调用组成,构成链式调用结构

    Table sensorTable = tableEnv.from("inputTable");
    Table resultTable = sensorTable
      .select("id","temperature")
      .filter("id = 'sensor_1'");

从文件获取数据

  • java代码

    package apitest.tableapi;
    
    
    import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
    import org.apache.flink.table.api.DataTypes;
    import org.apache.flink.table.api.Table;
    import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
    import org.apache.flink.table.descriptors.Csv;
    import org.apache.flink.table.descriptors.FileSystem;
    import org.apache.flink.table.descriptors.Schema;
    import org.apache.flink.types.Row;
    
    /**
     * @author : Ashiamd email: ashiamd@foxmail.com
     * @date : 2021/2/3 3:56 PM
     */
    public class TableTest2_CommonApi {
      public static void main(String[] args) throws Exception {
        // 创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 设置并行度为1
        env.setParallelism(1);
    
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
    
        // 2. 表的创建:连接外部系统,读取数据
        // 2.1 读取文件
        String filePath = "/tmp/Flink_Tutorial/src/main/resources/sensor.txt";
    
        tableEnv.connect(new FileSystem().path(filePath))
          .withFormat(new Csv())
          .withSchema(new Schema()
                      .field("id", DataTypes.STRING())
                      .field("timestamp", DataTypes.BIGINT())
                      .field("temp", DataTypes.DOUBLE())
                     )
          .createTemporaryTable("inputTable");
    
        Table inputTable = tableEnv.from("inputTable");
        //        inputTable.printSchema();
        //        tableEnv.toAppendStream(inputTable, Row.class).print();
    
        // 3. 查询转换
        // 3.1 Table API
        // 简单转换
        Table resultTable = inputTable.select("id, temp")
          .filter("id === 'sensor_6'");
    
        // 聚合统计
        Table aggTable = inputTable.groupBy("id")
          .select("id, id.count as count, temp.avg as avgTemp");
    
        // 3.2 SQL
        tableEnv.sqlQuery("select id, temp from inputTable where id = 'senosr_6'");
        Table sqlAggTable = tableEnv.sqlQuery("select id, count(id) as cnt, avg(temp) as avgTemp from inputTable group by id");
    
        // 打印输出
        tableEnv.toAppendStream(resultTable, Row.class).print("result");
        tableEnv.toRetractStream(aggTable, Row.class).print("agg");
        tableEnv.toRetractStream(sqlAggTable, Row.class).print("sqlagg");
    
        env.execute();
      }
    }
  • 输出结果

    里面的false表示上一条保存的记录被删除,true则是新加入的数据

    所以Flink的Table API在更新数据时,实际是先删除原本的数据,再添加新数据。

    result> sensor_6,15.4
    sqlagg> (true,sensor_1,1,35.8)
    sqlagg> (true,sensor_6,1,15.4)
    sqlagg> (true,sensor_7,1,6.7)
    sqlagg> (true,sensor_10,1,38.1)
    agg> (true,sensor_1,1,35.8)
    agg> (true,sensor_6,1,15.4)
    sqlagg> (false,sensor_1,1,35.8)
    sqlagg> (true,sensor_1,2,36.05)
    agg> (true,sensor_7,1,6.7)
    sqlagg> (false,sensor_1,2,36.05)
    sqlagg> (true,sensor_1,3,34.96666666666666)
    agg> (true,sensor_10,1,38.1)
    sqlagg> (false,sensor_1,3,34.96666666666666)
    sqlagg> (true,sensor_1,4,35.5)
    agg> (false,sensor_1,1,35.8)
    agg> (true,sensor_1,2,36.05)
    agg> (false,sensor_1,2,36.05)
    agg> (true,sensor_1,3,34.96666666666666)
    agg> (false,sensor_1,3,34.96666666666666)
    agg> (true,sensor_1,4,35.5)

读写Kafka

Kafka作为消息队列,和文件系统类似的,只能往里追加数据,不能修改数据。

  • 测试代码

    (我用的新版Flink和新版kafka连接器,所以version指定"universal")

    package apitest.tableapi;
    
    import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
    import org.apache.flink.table.api.DataTypes;
    import org.apache.flink.table.api.Table;
    import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
    import org.apache.flink.table.descriptors.Csv;
    import org.apache.flink.table.descriptors.Kafka;
    import org.apache.flink.table.descriptors.Schema;
    
    /**
     * @author : Ashiamd email: ashiamd@foxmail.com
     * @date : 2021/2/3 6:33 PM
     */
    public class TableTest4_KafkaPipeLine {
      public static void main(String[] args) throws Exception {
        // 1. 创建环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
    
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
    
        // 2. 连接Kafka,读取数据
        tableEnv.connect(new Kafka()
                         .version("universal")
                         .topic("sensor")
                         .property("zookeeper.connect", "localhost:2181")
                         .property("bootstrap.servers", "localhost:9092")
                        )
          .withFormat(new Csv())
          .withSchema(new Schema()
                      .field("id", DataTypes.STRING())
                      .field("timestamp", DataTypes.BIGINT())
                      .field("temp", DataTypes.DOUBLE())
                     )
          .createTemporaryTable("inputTable");
    
        // 3. 查询转换
        // 简单转换
        Table sensorTable = tableEnv.from("inputTable");
        Table resultTable = sensorTable.select("id, temp")
          .filter("id === 'sensor_6'");
    
        // 聚合统计
        Table aggTable = sensorTable.groupBy("id")
          .select("id, id.count as count, temp.avg as avgTemp");
    
        // 4. 建立kafka连接,输出到不同的topic下
        tableEnv.connect(new Kafka()
                         .version("universal")
                         .topic("sinktest")
                         .property("zookeeper.connect", "localhost:2181")
                         .property("bootstrap.servers", "localhost:9092")
                        )
          .withFormat(new Csv())
          .withSchema(new Schema()
                      .field("id", DataTypes.STRING())
                      //                        .field("timestamp", DataTypes.BIGINT())
                      .field("temp", DataTypes.DOUBLE())
                     )
          .createTemporaryTable("outputTable");
    
        resultTable.insertInto("outputTable");
    
        tableEnv.execute("");
      }
    }
  • 启动kafka目录里自带的zookeeper

    $ bin/zookeeper-server-start.sh config/zookeeper.properties
  • 启动kafka服务

    $ bin/kafka-server-start.sh config/server.properties
  • 新建kafka生产者和消费者

    $ bin/kafka-console-producer.sh --broker-list localhost:9092  --topic sensor
    
    $ bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic sinktest
  • 启动Flink程序,在kafka生产者console中输入数据,查看输出

    • 输入(kafka-console-producer)

      >sensor_1,1547718199,35.8
      >sensor_6,1547718201,15.4
      >sensor_7,1547718202,6.7
      >sensor_10,1547718205,38.1
      >sensor_6,1547718209,34.5
    • 输出(kafka-console-consumer)

      代码中,只筛选id为sensor_6的,所以输出没有问题

      sensor_6,15.4           
      
      sensor_6,34.5
      

更新模式

  • 对于流式查询,需要声明如何在表和外部连接器之间执行转换
  • 与外部系统交换的消息类型,由更新模式(Uadate Mode)指定
  • 追加(Append)模式
    • 表只做插入操作,和外部连接器只交换插入(Insert)消息
  • 撤回(Retract)模式
    • 表和外部连接器交换添加(Add)和撤回(Retract)消息
    • 插入操作(Insert)编码为Add消息;删除(Delete)编码为Retract消息;更新(Update)编码为上一条的Retract和下一条的Add消息
  • 更新插入(Upsert)模式
    • 更新和插入都被编码为Upsert消息;删除编码为Delete消息

输出到ES

  • 可以创建Table来描述ES中的数据,作为输出的TableSink

    tableEnv.connect(
      new Elasticsearch()
      .version("6")
      .host("localhost",9200,"http")
      .index("sensor")
      .documentType("temp")
    )
      .inUpsertMode()
      .withFormat(new Json())
      .withSchema(new Schema()
                  .field("id",DataTypes.STRING())
                  .field("count",DataTypes.BIGINT())
                 )
      .createTemporaryTable("esOutputTable");
    aggResultTable.insertInto("esOutputTable");

输出到MySQL

  • 需要的pom依赖

    Flink专门为Table API的jdbc连接提供了flink-jdbc连接器,需要先引入依赖

    <!-- https://mvnrepository.com/artifact/org.apache.flink/flink-jdbc -->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-jdbc_2.12</artifactId>
        <version>1.10.3</version>
        <scope>provided</scope>
    </dependency>
  • 可以创建Table来描述MySql中的数据,作为输入和输出

    String sinkDDL = 
      "create table jdbcOutputTable (" +
      " id varchar(20) not null, " +
      " cnt bigint not null " +
      ") with (" +
      " 'connector.type' = 'jdbc', " +
      " 'connector.url' = 'jdbc:mysql://localhost:3306/test', " +
      " 'connector.table' = 'sensor_count', " +
      " 'connector.driver' = 'com.mysql.jdbc.Driver', " +
      " 'connector.username' = 'root', " +
      " 'connector.password' = '123456' )";
    tableEnv.sqlUpdate(sinkDDL);    // 执行DDL创建表
    aggResultSqlTable.insertInto("jdbcOutputTable");

表和流的转换

Flink- 将表转换成DataStream | 查看执行计划 | 流处理和关系代数的区别 | 动态表 | 流式持续查询的过程 | 将流转换成动态表 | 持续查询 | 将动态表转换成 DS

将Table转换成DataStream

  • 表可以转换为 DataStream 或 DataSet ,这样自定义流处理或批处理程序就可以继续在 Table API 或 SQL 查询的结果上运行了

  • 将表转换为 DataStream 或 DataSet 时,需要指定生成的数据类型,即要将表的每一行转换成的数据类型

  • 表作为流式查询的结果,是动态更新的

  • 转换有两种转换模式:追加(Appende)模式和撤回(Retract)模式

  • 追加模式

    • 用于表只会被插入(Insert)操作更改的场景
    DataStream<Row> resultStream = tableEnv.toAppendStream(resultTable,Row.class);
  • 撤回模式

    • 用于任何场景。有些类似于更新模式中Retract模式,它只有Insert和Delete两类操作。

    • 得到的数据会增加一个Boolean类型的标识位(返回的第一个字段),用它来表示到底是新增的数据(Insert),还是被删除的数据(Delete)

      (更新数据,会先删除旧数据,再插入新数据)

将DataStream转换成表

  • 对于一个DataStream,可以直接转换成Table,进而方便地调用Table API做转换操作

    DataStream<SensorReading> dataStream = ...
    Table sensorTable = tableEnv.fromDataStream(dataStream);
  • 默认转换后的Table schema和DataStream中的字段定义一一对应,也可以单独指定出来

    DataStream<SensorReading> dataStream = ...
    Table sensorTable = tableEnv.fromDataStream(dataStream,
                                               "id, timestamp as ts, temperature");

创建临时视图(Temporary View)

  • 基于DataStream创建临时视图

    tableEnv.createTemporaryView("sensorView",dataStream);
    tableEnv.createTemporaryView("sensorView",
                                dataStream, "id, timestamp as ts, temperature");
  • 基于Table创建临时视图

    tableEnv.createTemporaryView("sensorView", sensorTable);

查看执行计划

  • Table API 提供了一种机制来解释计算表的逻辑和优化查询计划

  • 查看执行计划,可以通过TableEnvironment.explain(table)方法或TableEnvironment.explain()方法完成,返回一个字符串,描述三个计划

    • 优化的逻辑查询计划
    • 优化后的逻辑查询计划
    • 实际执行计划
String explaination = tableEnv.explain(resultTable);
System.out.println(explaination);

流处理和关系代数的区别

Flink- 将表转换成DataStream | 查看执行计划 | 流处理和关系代数的区别 | 动态表 | 流式持续查询的过程 | 将流转换成动态表 | 持续查询 | 将动态表转换成 DS

​ Table API和SQL,本质上还是基于关系型表的操作方式;而关系型表、关系代数,以及SQL本身,一般是有界的,更适合批处理的场景。这就导致在进行流处理的过程中,理解会稍微复杂一些,需要引入一些特殊概念。

​ 可以看到,其实关系代数(主要就是指关系型数据库中的表)和SQL,主要就是针对批处理的,这和流处理有天生的隔阂。

关系代数(表)/SQL流处理
处理的数据对象字段元组的有界集合字段元组的无限序列
查询(Query)对数据的访问可以访问到完整的数据输入无法访问所有数据,必须持续"等待"流式输入
查询终止条件生成固定大小的结果集后终止永不停止,根据持续收到的数据不断更新查询结果

动态表(Dynamic Tables)

​ 我们可以随着新数据的到来,不停地在之前的基础上更新结果。这样得到的表,在Flink Table API概念里,就叫做“动态表”(Dynamic Tables)。

  • 动态表是 Flink 对流数据的 Table API 和 SQL 支持的核心概念
  • 与表示批处理数据的静态表不同,动态表是随时间变化的
  • 持续查询(Continuous Query)
    • 动态表可以像静态的批处理表一样进行查询,查询一个动态表会产生持续查询(Continuous Query)
    • 连续查询永远不会终止,并会生成另一个动态表
    • 查询(Query)会不断更新其动态结果表,以反映其动态输入表上的更改。

动态表和持续查询

在这里插入图片描述

流式表查询的处理过程:

  1. 流被转换为动态表

  2. 对动态表计算连续查询,生成新的动态表

  3. 生成的动态表被转换回流

将流转换成动态表

  • 为了处理带有关系查询的流,必须先将其转换为表
  • 从概念上讲,流的每个数据记录,都被解释为对结果表的插入(Insert)修改操作

本质上,我们其实是从一个、只有插入操作的changelog(更新日志)流,来构建一个表

来一条数据插入一条数据

在这里插入图片描述

持续查询

  • 持续查询,会在动态表上做计算处理,并作为结果生成新的动态表。

    与批处理查询不同,连续查询从不终止,并根据输入表上的更新更新其结果表。

    在任何时间点,连续查询的结果在语义上,等同于在输入表的快照上,以批处理模式执行的同一查询的结果。

​ 下图为一个点击事件流的持续查询,是一个分组聚合做count统计的查询。

在这里插入图片描述

将动态表转换成 DataStream

  • 与常规的数据库表一样,动态表可以通过插入(Insert)、更新(Update)和删除(Delete)更改,进行持续的修改
  • 将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码

  • 仅追加(Append-only)流

    • 仅通过插入(Insert)更改来修改的动态表,可以直接转换为仅追加流
  • 撤回(Retract)流

    • 撤回流是包含两类消息的流:添加(Add)消息和撤回(Retract)消息

      动态表通过将INSERT 编码为add消息、DELETE 编码为retract消息、UPDATE编码为被更改行(前一行)的retract消息和更新后行(新行)的add消息,转换为retract流。

  • Upsert(更新插入流)

    • Upsert流也包含两种类型的消息:Upsert消息和删除(Delete)消息

      通过将INSERT和UPDATE更改编码为upsert消息,将DELETE更改编码为DELETE消息,就可以将具有唯一键(Unique Key)的动态表转换为流。

将动态表转换成DataStream

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值