目录



时态表(Temporal Table)是一张随时间变化的表 – 在 Flink 中称为动态表,时态表中的每条记录都关联了一个或多个时间段,所有的 Flink 表都是时态的(动态的)。


时态表包含表的一个或多个有版本的表快照,时态表可以是一张跟踪所有变更记录的表(例如数据库表的 changelog,包含多个表快照),也可以是物化所有变更之后的表(例如数据库表,只有最新表快照)。

版本: 时态表可以划分成一系列带版本的表快照集合,表快照中的版本代表了快照中所有记录的有效区间,有效区间的开始时间和结束时间可以通过用户指定,根据时态表是否可以追踪自身的历史版本与否,时态表可以分为 版本表 和 普通表。

版本表: 如果时态表中的记录可以追踪和并访问它的历史版本,这种表我们称之为版本表,来自数据库的 changelog 可以定义成版本表。

普通表: 如果时态表中的记录仅仅可以追踪并和它的最新版本,这种表我们称之为普通表,来自数据库 或 HBase 的表可以定义成普通表。

注意 仅 Blink planner 支持此功能。

flink sink的表结构变化 flink 动态表_apache


flink sink的表结构变化 flink 动态表_java_02


flink sink的表结构变化 flink 动态表_apache_03

flink sink的表结构变化 flink 动态表_apache_04


如果流延迟过来的数据要跟之前的维表数据做关联,即根据流的事件时间,查找某个时间点的维度数据而不是当前维度表数据。

比如这样一个场景:用户的订单表和和商品维度表,将维度表设置成时态表,这样用户就可以根据订单表中的下单时间Join下单时的商品当时最新的维度数据

Flink引入了temporal table, Temporal Table是Append Only表(只允许插入记录,而不会修改、删除记录)上的物化视图,它把Append Only的表变化解释为表的Changelog,并提供特定时间版本的表数据、将每个时间点映射到静态关系中,表的版本数据存储在状态里。在Flink中,通过TemporalTableFunction实现对temporal table的访问,必须定义主键和时间戳属性,主键确定覆盖哪些行,时间戳确定有效的时间,也就是表的数据版本。

时态表代码开发:
数据源:ratesHistory.csv

RMB,114,2015-01-01 00:00:00
RMB,115,2015-01-03 00:00:00
RMB,116,2015-01-19 00:00:00
Euro,119,2015-01-03 00:00:00
USD,99,2015-01-03 00:00:00
USD,100,2015-01-03 00:00:00
Euro,118,2015-01-03 00:00:00

数据源:rateOrder.csv

1,29,RMB,2015-01-02 00:00:00
2,19,RMB,2015-01-03 00:00:00
3,33,RMB,2015-01-11 00:00:00
4,55,RMB,2015-01-21 00:00:00
package com.aikfk.flink.sql;

import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.types.Row;

/**
 * @author :caizhengjie
 * @description:TODO
 * @date :2021/4/5 10:32 下午
 */
public class TemporalTableJoinEventTime {
    public static void main(String[] args) throws Exception {
        // 1.准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 2.创建TableEnvironment(Blink planner)
        EnvironmentSettings settings = EnvironmentSettings.newInstance().inStreamingMode().useBlinkPlanner().build();
        StreamTableEnvironment tableEnvironment = StreamTableEnvironment.create(env , settings);

        // 3.文件path
        String ratesHistoryPath = "/Users/caizhengjie/IdeaProjects/aikfk_flink/src/main/java/resources/ratesHistory.csv";
        String ratesOrderPath = "/Users/caizhengjie/IdeaProjects/aikfk_flink/src/main/java/resources/rateOrder.csv";


        // 4.DDL
        String ratesHistory_ddl =
                "create table currency_rates (\n" +
                        " currency STRING,\n" +
                        " conversion_rate DECIMAL(32, 2),\n" +
                        " update_time TIMESTAMP(3),\n" +
                        " PRIMARY KEY (currency) NOT ENFORCED,\n" +
                        " WATERMARK FOR update_time AS update_time \n" +
                        ") WITH (\n" +
                        " 'connector.type' = 'filesystem',\n" +
                        " 'connector.path' = '"+ratesHistoryPath+"',\n" +
                        " 'format.type' = 'csv'\n" +
                        ")";

        tableEnvironment.executeSql(ratesHistory_ddl);


        String ratesOrder_DDL =
                "create table orders (\n" +
                        " order_id    STRING,\n" +
                        " price       DECIMAL(32,2),\n" +
                        " currency    STRING,\n" +
                        " order_time  TIMESTAMP(3),\n" +
                        " WATERMARK FOR order_time AS order_time \n" +
                        ") WITH (\n" +
                        " 'connector.type' = 'filesystem',\n" +
                        " 'connector.path' = '"+ratesOrderPath+"',\n" +
                        " 'format.type' = 'csv'\n" +
                        ")";

        tableEnvironment.executeSql(ratesOrder_DDL);


        // 6.通过SQL对表的查询,生成结果表
        String sql =
                "SELECT \n" +
                        "     order_id,\n" +
                        "     price,\n" +
                        "     orders.currency,\n" +
                        "     conversion_rate,\n" +
                        "     order_time\n" +
                        " FROM orders\n" +
                        " LEFT JOIN currency_rates FOR SYSTEM_TIME AS OF orders.order_time\n" +
                        " ON orders.currency = currency_rates.currency";

        Table table = tableEnvironment.sqlQuery(sql);

        // 7.将table表转换为DataStream
        DataStream<Tuple2<Boolean, Row>> retractStream = tableEnvironment.toRetractStream(table, Row.class);
        retractStream.print();
        env.execute();

        /**
         * 2> (true,1,29.00,RMB,114.00,2015-01-02T00:00)
         * 2> (true,4,55.00,RMB,116.00,2015-01-21T00:00)
         * 2> (true,2,19.00,RMB,115.00,2015-01-03T00:00)
         * 2> (true,3,33.00,RMB,115.00,2015-01-11T00:00)
         */

    }
}

关于时态表的官方文档:

关于流上join的官方文档:


以上内容仅供参考学习,如有侵权请联系我删除!