flink Table & SQL概述与实例

简介

Flink 自身提供了不同级别的抽象来支持我们开发流式或者批量处理程序,下图描述了 Flink 支持的 4 种不同级别的抽象。
在这里插入图片描述
Table API 和 SQL 处于最顶端,是 Flink 提供的高级 API 操作。Flink SQL 是 Flink 实时计算的简化计算模型,降低用户使用实时计算门槛而设计的一套符合标准 SQL 语义的开发语言。

Flink 在编程模型上提供了 DataStream 和 DataSet 两套 API,并没有做到事实上的流批统一,用户和开发者开发了两套代码。正是因为 Flink Table & SQL 的加入, Flink 在某种程度上做到了事实上的批流一体。

与传统的SQL查询相比,Flink SQL是动态表查询,SQL不会中止,会不断的执行,且增加了窗口和排序。FLink从 0.9 版本中支持FLink SQL,但是目前为止,Flink SQL和Table没有支持完全的业务场景,有些场景没有调优等。所以需要到官网进行查询是否支持业务场景。

原理

Hive的底层对 SQL 的解析用到了 Apache Calcite,Flink 同样把 SQL 的解析、优化和执行交给了Calcite。
下图是一张经典的 Flink Table & SQL 实现原理图,可以看到 Calcite 在整个架构中处于绝对核心地位。
在这里插入图片描述
从图中可以看到无论是批查询 SQL 还是流式查询 SQL,都会经过对应的转换器 Parser 转换成为节点树 SQLNode tree,然后生成逻辑执行计划 Logical Plan,逻辑执行计划在经过优化后生成真正可以执行的物理执行计划,交给 DataSet 或者 DataStream 的 API 去执行。
一个完整的 Flink Table & SQL Job 也是由 Source、Transformation、Sink 构成:

在这里插入图片描述
Source 部分来源于外部数据源,我们经常用的有 Kafka、MySQL 等;
Transformation 部分则是 Flink Table & SQL 支持的常用 SQL 算子,比如简单的 Select、Groupby 等,当然在这里也有更为复杂的多流 Join、流与维表的 Join 等;
Sink 部分是指的结果存储比如 MySQL、HBase 或 Kakfa 等。

Flink Table & SQL案例

本实例自定义源源不断的数据源,模拟student信息,根据id的奇偶筛选出两批学生。将数据输入到注册的临时表中,并将两表join,查询输出到控制台。

pom依赖

Table & SQL API依赖说明:

  • flink-table-common: 公共模块,比如自定义函数、格式等需要依赖的。
  • flink-table-api-java: Table 和 SQL API,使用 Java 语言编写的,给纯 table 程序使用(还在早期开发阶段,不建议使用)
  • flink-table-api-scala: Table 和 SQL API,使用 Scala 语言编写的,给纯 table 程序使用(还在早期开发阶段,不建议使用)
  • flink-table-api-java-bridge: Table 和 SQL API 结合 DataStream/DataSet API 一起使用,给 Java 语言使用。
  • flink-table-api-scala-bridge: Table 和 SQL API 结合 DataStream/DataSet API 一起使用,给 Scala 语言使用。
  • flink-table-planner: table Planner 和运行时。这是在1.9之前 Flink 的唯一的 Planner,但是从1.11版本开始我们不推荐继续使用。
  • flink-table-planner-blink: 新的 Blink Planner,从1.11版本开始成为默认的 Planner。
  • flink-table-runtime-blink: 新的 Blink 运行时。
  • flink-table-uber: 把上述模块以及 Old Planner 打包到一起,可以在大部分 Table & SQL API 场景下使用。打包到一起的 jar 文件 flink-table-*.jar 默认会直接放到 Flink 发行版的 /lib 目录下。
  • flink-table-uber-blink: 把上述模块以及 Blink Planner 打包到一起,可以在大部分 Table & SQL API 场景下使用。打包到一起的 jar 文件 flink-table-blink-*.jar 默认会放到 Flink 发行版的 /lib 目录下。

将本实例的flink程序相关的依赖加进来,此时在本地idea环境下运行:

		<dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-java_2.11</artifactId>
            <version>1.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-api-java</artifactId>
            <version>1.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-api-java-bridge_2.11</artifactId>
            <version>1.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-planner-blink_2.11</artifactId>
            <version>1.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-runtime-blink_2.11</artifactId>
            <version>1.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-common</artifactId>
            <version>1.10.0</version>
        </dependency>

Student数据类型实现

import lombok.Data;

@Data
public class Student {
    int id;
    String name;
}

数据源实现

此时需要模拟一个实时的数据流。

import org.apache.flink.streaming.api.functions.source.SourceFunction;
import java.util.ArrayList;
import java.util.Random;

class MyStreamingSource implements SourceFunction<Student> {

    private boolean isRunning = true;

    @Override
    public void run(SourceContext<Student> ctx) throws Exception {
        while (isRunning) {
            Student student = generateStudent();
            ctx.collect(student);
            //睡一会
            Thread.sleep(1000);
        }
    }

    @Override
    public void cancel() {
        isRunning = false;
    }

    private Student generateStudent() {
        int i = new Random().nextInt(1000);
        ArrayList<String> list = new ArrayList<>();
        list.add("LaoLiu");
        list.add("LaoXu");
        list.add("LaoWang");
        list.add("LaoTan");

        Student student = new Student();
        student.setName(list.get(new Random().nextInt(4)));
        student.setId(i);

        return student;
    }
}

案例实现

import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.tuple.Tuple4;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.datastream.SplitStream;
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.java.StreamTableEnvironment;

import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) throws Exception {
        EnvironmentSettings bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
        StreamExecutionEnvironment bsEnv = StreamExecutionEnvironment.getExecutionEnvironment();
        StreamTableEnvironment bsTableEnv = StreamTableEnvironment.create(bsEnv, bsSettings);

        SingleOutputStreamOperator<Student> source = bsEnv.addSource(new MyStreamingSource());

        DataStream<Student> splitAll = source.split(student -> {
            ArrayList<String> output = new ArrayList<>();
            if (student.getId() % 2 == 0) {
                output.add("even");
            } else {
                output.add("odd");
            }
            return output;
        });

        //将两个流筛选出来
        DataStream<Student> evenSelect = ((SplitStream<Student>) splitAll).select("even");
        DataStream<Student> oddSelect = ((SplitStream<Student>) splitAll).select("odd");

        //把这两个流在我们的Flink环境中注册为临时表
        bsTableEnv.createTemporaryView("default_catalog.default_database.evenTable", evenSelect, "name,id");
        bsTableEnv.createTemporaryView("default_catalog.default_database.oddTable", oddSelect, "name,id");

        Table queryTable = bsTableEnv.sqlQuery("select a.id,a.name,b.id,b.name from evenTable as a join oddTable as b on a.name=b.name");

        queryTable.printSchema();
		// 把表转化成流
        bsTableEnv.toRetractStream(queryTable, TypeInformation.of(new TypeHint<Tuple4<Integer, String, Integer, String>>() {
        })).print();

        bsEnv.execute("demo");
    }
}

输出结果如下:
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值