Flink 基础概念和特性

361 阅读24分钟

Flink 简介

Flink 起源

Flink起源于Stratosphere项目,2010~2014年由3所地处柏林的大学和欧洲的一些其他的大学共同进行的研究项目,2014年4月Stratosphere的代码被复制并捐赠给了Apache软件基金会,参加这个孵化项目的初始成员是Stratosphere系统的核心开发人员,2014年12月,Flink一跃成为Apache软件基金会的顶级项目。

image-20210707114156967.png

Flink项目的理念是:“Apache Flink是为分布式、高性能、随时可用以及准确的流处理应用程序打造的开源流处理框架”。

Flink是什么?

Flink 是一个 高吞吐、低延时、高性能、有状态的高可用的分布式计算框架

高吞吐: 异步&&分布式快照&&管道

低延时: Native Streaming本地流

高性能:内存优化,自动容错

有状态: 容错 与 Exactly Once 语义

高可用: 支持ZK的高可用机制

分布式: Master-Slave 架构,横向扩展

Flink 特性

事件驱动型(Event-driven)

事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。比较典型的就是以kafka为代表的消息队列几乎都是事件驱动型应用。

流批一体

批处理的特点是有界、持久、大量,非常适合需要访问全套记录才能完成的计算工作,一般用于离线统计。

流处理的特点是无界、实时, 无需针对整个数据集执行操作,而是对通过系统传输的每个数据项执行操作,一般用于实时统计。

spark的世界观中,一切都是由批次组成的,离线数据是一个大批次,而实时数据是由一个一个无限的小批次组成的。

而在flink的世界观中,一切都是由流组成的,离线数据是有界限的流,实时数据是一个没有界限的流,这就是所谓的有界流和无界流。

无界数据流:无界数据流有一个开始但是没有结束,它们不会在生成时终止并提供数据,必须连续处理无界流,也就是说必须在获取后立即处理event。对于无界数据流我们无法等待所有数据都到达,因为输入是无界的,并且在任何时间点都不会完成。处理无界数据通常要求以特定顺序(例如事件发生的顺序)获取event,以便能够推断结果完整性。

有界数据流:有界数据流有明确定义的开始和结束,可以在执行任何计算之前通过获取所有数据来处理有界流,处理有界流不需要有序获取,因为可以始终对有界数据集进行排序,有界流的处理也称为批处理。

延伸 大数据计算的架构对比

Lambda 架构

Lambda 架构由Storm的作者Nathan Marz提出,其设计目的在于提供一个能满足大数据系统关键特性的架构,包括高容错、低延迟、可扩展等。其整合离线计算与实时计算,融合不可变性、读写分离和复杂性隔离等原则,可集成Hadoop, Kafka, Spark,Storm等各类大数据组件。

Lambda 架构可分解为三层Layer,即Batch Layer, Real-Time(Speed) Layer和Serving Layer。

  1. Batch Layer : 存储数据集,在数据集上预先计算查询函数,并构建查询所对应的View。Batch Layer可以很好的处理离线数据,但有很多场景数据是不断实时生成且需要实时查询处理,对于这情况, Speed Layer更为适合。
  2. Speed Layer : Batch Layer处理的是全体数据集,而Speed Layer处理的是最近的增量数据流。Speed Layer为了效率,在接收到新的数据后会不断更新Real-time View,而Batch Layer是根据全体离线数据集直接得到Batch View。
  3. Serving Layer : Serving Layer用于合并Batch View和Real-time View中的结果数据集到最终数据集。

1735488-20200307160425249-287686577.png

1735488-20200307160455182-1669835581.png

Kappa 架构

Kappa 架构是LinkedIn的Jay Kreps结合实际经验和个人体会,针对Lambda架构进行深度剖析,分析其优缺点并采用的替代方案。Lambda 架构的一个很明显的问题是需要维护两套分别跑在批处理和实时计算系统上面的代码,而且这两套代码得产出一模一样的结果。因此对于设计这类系统的人来讲,要面对的问题是:为什么我们不能改进流计算系统让它能处理这些问题?为什么不能让流系统来解决数据全量处理的问题?流计算天然的分布式特性注定其扩展性比较好,能否加大并发量来处理海量的历史数据?基于种种问题的考虑,Jay提出了Kappa这种替代方案。

1735488-20200307160543178-1049868979.png

那如何用流计算系统对全量数据进行重新计算,步骤如下:

  1. 用Kafka或类似的分布式队列保存数据,需要几天数据量就保存几天。
  2. 当需要全量计算时,重新起一个流计算实例,从头开始读取数据进行处理,并输出到一个结果存储中。
  3. 当新的实例完成后,停止老的流计算实例,并把老的一引起结果删除。

1735488-20200307160616150-886226726.png

有状态计算和Event Time 语义

有状态的算子

Flink内置的很多算子,数据源source,数据存储sink都是有状态的,流中的数据都是buffer records,会保存一定的元素或者元数据。

在Flink中,状态始终与特定算子相关联。总的来说,有两种类型的状态:

算子状态(operator state)

算子状态的作用范围限定为算子任务。这意味着由同一并行任务所处理的所有数据都可以访问到相同的状态,状态对于同一任务而言是共享的。算子状态不能由相同或不同算子的另一个任务访问。

  • 列表状态(List state)

    将状态表示为一组数据的列表。

  • 联合列表状态(Union list state)

    也将状态表示为数据的列表。它与常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启动应用程序时如何恢复。

  • 广播状态(Broadcast state)

    如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态

键控状态(keyed state)

键控状态是根据输入数据流中定义的键(key)来维护和访问的。Flink为每个键值维护一个状态实例,并将具有相同键的所有数据,都分区到同一个算子任务中,这个任务会维护和处理这个key对应的状态。当任务处理一条数据时,它会自动将状态的访问范围限定为当前数据的key。因此,具有相同key的所有数据都会访问相同的状态。Keyed State很类似于一个分布式的key-value map数据结构,只能用于KeyedStream(keyBy算子处理之后)

  • ValueState[T] 保存单个的值,值的类型为T。

    • get操作: ValueState.value()
    • set操作: ValueState.update(value: T)
  • ListState[T] 保存一个列表,列表里的元素的数据类型为T。基本操作如下:

    • ListState.add(value: T)
    • ListState.addAll(values: java.util.List[T])
    • ListState.get()返回Iterable[T]
    • ListState.update(values: java.util.List[T])
  • MapState[K, V] 保存Key-Value对。

    • MapState.get(key: K)
    • MapState.put(key: K, value: V)
    • MapState.contains(key: K)
    • MapState.remove(key: K)
  • ReducingState[T]

  • AggregatingState[I, O]

状态一致性

一致性级别

  • at-most-once: 这其实是没有正确性保障的委婉说法——故障发生之后,计数结果可能丢失。同样的还有udp。
  • at-least-once: 这表示计数结果可能大于正确值,但绝不会小于正确值。也就是说,计数程序在发生故障后可能多算,但是绝不会少算。
  • exactly-once: 这指的是系统保证在发生故障后得到的计数结果与正确值一致。

端到端(end-to-end)状态一致性

  • 内部保证 —— 依赖checkpoint
  • source 端 —— 需外部源,可重设数据的读取位置
  • sink 端 —— 需保证,从故障恢复时,数据不会重复写入外部系统

而对于sink端,又有两种具体的实现方式:幂等(Idempotent)写入和事务性(Transactional)写入。

  • 幂等写入

所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改,也就是说,后面再重复执行就不起作用了。

  • 事务写入

需要构建事务来写入外部系统,构建的事务对应着 checkpoint,等到 checkpoint 真正完成的时候,才把所有对应的结果写入 sink 系统中。

对于事务性写入,具体又有两种实现方式:预写日志(WAL)和两阶段提交(2PC)。DataStream API 提供了GenericWriteAheadSink模板类和TwoPhaseCommitSinkFunction 接口,可以方便地实现这两种方式的事务性写入。

不同 Source 和 Sink 的一致性保证可以用下表说明:

image-20210707120558551.png

检查点(checkpoint)

Flink具体如何保证exactly-once呢? 它使用一种被称为"检查点"(checkpoint)的特性,在出现故障时将系统重置回正确状态。

Flink检查点的核心作用是确保状态正确,即使遇到程序中断,也要正确。记住这一基本点之后,我们用一个例子来看检查点是如何运行的。Flink为用户提供了用来定义状态的工具。

Flink+Kafka如何实现端到端的exactly-once语义

端到端的状态一致性的实现,需要每一个组件都实现,对于Flink + Kafka的数据管道系统(Kafka进、Kafka出)而言,各组件怎样保证exactly-once语义呢???

  • 内部 —— 利用checkpoint机制,把状态存盘,发生故障的时候可以恢复,保证内部的状态一致性
  • source —— kafka consumer作为source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候可以由连接器重置偏移量,重新消费数据,保证一致性
  • sink —— kafka producer作为sink,采用两阶段提交 sink,需要实现一个 TwoPhaseCommitSinkFunction
状态后端(state backend)
  • MemoryStateBackend

内存级的状态后端,会将键控状态作为内存中的对象进行管理,将它们存储在TaskManager的JVM堆上;而将checkpoint存储在JobManager的内存中。

  • FsStateBackend

将checkpoint存到远程的持久化文件系统(FileSystem)上。而对于本地状态,跟MemoryStateBackend一样,也会存在TaskManager的JVM堆上。

  • RocksDBStateBackend

将所有状态序列化后,存入本地的RocksDB中存储。支持增量

时间语义
Flink中的时间语义
  • Event Time:是事件创建的时间。它通常由事件中的时间戳描述,例如采集的日志数据中,每一条日志都会记录自己的生成时间,Flink通过时间戳分配器访问事件时间戳。
  • Ingestion Time:是数据进入Flink的时间。Ingestion摄取
  • Processing Time:是每一个执行基于时间操作的算子的本地系统时间,与机器相关,默认的时间属性就是Processing Time。
Watermark水位线

产生原因

流处理从事件产生,到流经source,再到operator,中间是有一个过程和时间的,虽然大部分情况下,流到operator的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络、分布式等原因,导致乱序的产生,所谓乱序,就是指Flink接收到的事件的先后顺序不是严格按照事件的Event Time顺序排列的。

Watermark机制

Flink有个机制来保证一个特定的时间后,必须触发window去进行计算了,这个特别的机制,就是Watermark。

  • Watermark是一种衡量Event Time进展的机制。
  • Watermark是用于处理乱序事件的,而正确的处理乱序事件,通常用Watermark机制结合window来实现。
  • 数据流中的Watermark用于表示timestamp小于Watermark的数据,都已经到达了,因此,window的执行也是由Watermark触发的。
  • Watermark可以理解成一个延迟触发机制,我们可以设置Watermark的延时时长t,每次系统会校验已经到达的数据中最大的maxEventTime,然后认定eventTime小于maxEventTime - t的所有数据都已经到达,如果有窗口的停止时间等于maxEventTime – t,那么这个窗口被触发执行。
window窗口
  • 滚动窗口

image-20210707150455432.png

  • 滑动窗口

image-20210707150520416.png

  • 会话窗口

image-20210707150533541.png

Flink运行时架构

整体架构

image-20210707151159402.png

Flink运行时架构主要包括四个不同的组件,它们会在运行流处理应用程序时协同工作:作业管理器(JobManager)、资源管理器(ResourceManager)、任务管理器(TaskManager),以及分发器(Dispatcher)。

  • 作业管理器(JobManager)

    控制一个应用程序执行的主进程,也就是说,每个应用程序都会被各自的JobManager所控制执行。

    • JobManager先接收要执行的应用程序,这个应用程序会包括:作业图(JobGraph)、逻辑数据流图(logical dataflow graph)和打包了所有的类、库和其它资源的JAR包。JobManager会把JobGraph转换成一个物理层面的数据流图,这个图被叫做“执行图”(ExecutionGraph),包含了所有可以并发执行的任务。
    • JobManager会向资源管理器(ResourceManager)请求所需计算资源,也就是任务管理器(TaskManager)上的插槽(slot)。一旦它获取到了足够的资源,就会将执行图(ExecutionGraph)分发到真正运行它们的TaskManager上。
    • 调度和执行该Job的所有Task

    在运行过程中,JobManage负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。

  • 资源管理器(ResourceManager)

    主要负责管理任务管理器(TaskManager)的插槽(slot),TaskManger插槽是Flink中定义的处理资源单元。Flink为不同的环境和资源管理工具提供了不同资源管理器,比如YARN、Mesos、K8s,以及standalone部署。当JobManager申请插槽资源时,ResourceManager会将有空闲插槽的TaskManager分配给JobManager。如果ResourceManager没有足够的插槽来满足JobManager的请求,它还可以向资源提供平台发起会话,以提供启动TaskManager进程的容器。

  • 任务管理器(TaskManager)

    Flink中的工作进程。通常在Flink中会有多个TaskManager运行,每个TaskManager都包含了一定数量的插槽(slots)。插槽的数量限制了TaskManager能够执行的任务数量。启动之后,TaskManager会向资源管理器注册它的插槽;

    TaskManager收到资源管理器的指令后,就会将一个或多个插槽提供给JobManager调用。JobManager就可以向插槽分配任务(tasks)来执行了。在执行过程中,一个TaskManager可以跟其它运行同一应用程序的TaskManager交换数据。

  • 分发器(Dispatcher)

    可以跨作业运行,它为应用提交提供了REST接口。当一个应用被提交执行时,分发器就会启动并将应用移交给一个JobManager。由于是REST接口,所以Dispatcher可以作为集群的一个HTTP接入点,这样就能够不受防火墙阻挡。Dispatcher也会启动一个Web UI,用来方便地展示和监控作业执行的信息。Dispatcher在架构中并不是必需的,这取决于应用提交运行的方式。

image-20210707151639966.png

Flink任务提交

image-20210707153251577.png 比如提交到Yarn 集群

image-20210707153341484.png

1 Flink任务提交后,Client向HDFS上传Flink的Jar包和配置,

2 向Yarn ResourceManager提交任务,

3 ResourceManager分配Container资源并通知对应的NodeManager启动ApplicationMaster,

ApplicationMaster启动后加载Flink的Jar包和配置构建环境,然后启动JobManager,

4 JobManager启动之后ApplicationMaster向ResourceManager申请资源启动TaskManager,

ResourceManager分配Container资源,

5 由ApplicationMaster通知资源所在节点的NodeManager启动TaskManager,

NodeManager加载Flink的Jar包和配置构建环境并启动TaskManager,

TaskManager启动后向JobManager发送心跳包,并等待JobManager向其分配任务

任务调度原理

  • Client 为提交 Job 的客户端,可以是运行在任何机器上(与 JobManager 环境连通即可)。提交 Job 后,Client 可以结束进程(Streaming的任务),也可以不结束并等待结果返回。

  • JobManager 主要负责调度 Job 并协调 Task 做 checkpoint。从 Client 处接收到 Job 和 JAR 包等资源后,会生成优化后的执行计划,并以 Task 的单元调度到各个TaskManager 去执行。

  • TaskManager 在启动的时候就设置好了槽位数(Slot),每个 slot 能启动一个 Task,Task 为线程。从 JobManager 处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty 连接,接收数据并处理。

TaskManger与Slots
TaskManger

Flink中每一个worker(TaskManager)都是一个JVM进程**,它可能会在独立的线程上执行一个或多个subtask。

为了控制一个worker接收task的数量,worker通过task slot进行控制(一个worker至少有一个task slot)。

每个task slot表示TaskManager的资源大小固定的子集

在同一个JVM进程中的task共享TCP连接(基于多路复用)和心跳消息。它们也可能共享数据集和数据结构,因此这减少了每个task的负载。

Task Slot

Task Slot是静态的概念,是指TaskManager具有的并发执行能力,Slot数量可以通过参数taskmanager.numberOfTaskSlots进行配置;而并行度parallelism是动态概念,即TaskManager运行程序时实际使用的并发能力,可以通过参数parallelism.default进行配置。

执行图(ExecutionGraph)

由Flink程序直接映射成的数据流图是StreamGraph,也被称为逻辑流图,因为它们表示的是计算逻辑的高级视图。为了执行一个流处理程序,Flink需要将逻辑流图转换为物理数据流图(也叫执行图),详细说明程序的执行方式。

  • StreamGraph

    是根据用户通过 Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构

  • JobGraph

    StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。主要的优化为,将多个符合条件的节点 chain 在一起作为一个节点,这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。是优化后的StreamGraph

  • ExecutionGraph

    JobManager 根据 JobGraph 生成ExecutionGraph。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构

  • 物理执行图

    JobManager 根据ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。而是Job的Task在TaskManager部署情况

image-20210707154305222.png

任务链(Operator Chains)

相同并行度的one to one操作,Flink这样相连的算子链接在一起形成一个task,原来的算子成为里面的一部分。将算子链接成task是非常有效的优化:能减少线程之间的切换和基于缓存区的数据交换在减少时延的同时提升吞吐量。

image-20210707154354988.png

Flink部署

Standalone模式

一般用于开发测试阶段; 不依赖yarn 和 k8s 等,比较简单

集群模式

Session 模式
  • Runtime 集群组件共享
  • 资源复用
  • Runtime 中有多个 JobManager
Per-Job 模式
  • Runtime 集群组件仅为单个 Job 服务
  • 资源相对独立
  • 不支持提交 JobGraph

Flink Client

主要功能

image-20210707154553955.png

  • 集群管理

    • 集群的创建
    • 集群的停止
  • 任务管理

    • job 的提交
    • job 取消
    • 触发SavePoint
    • Job状态跟踪
  • JobGraph执行计划

    • 执行计划的解析;转换
    • 执行计划的优化

主要组件

image-20210707154627060.png

  • ClusterDescriptor

    直接对应各种集群的定义,描述和连接管理

  • PipelineExecutor

    DAG的执行计划,负责JobGraph的生成,转化,优化等。并提供JobGraph执行计划提交到各种不同的执行器进行执行

  • ContextEnvoronment

    执行环境上下文,定义了执行的Flink的配置读取,计算并行度等,各种任务算子DAG

Flink资源管理

TaskManager资源管理

image-20210707160550490.png

资源类型

  • 内存
  • CPU
  • 其他拓展资源

ResourceManager 资源管理

image-20210707160727772.png

双层资源调度

  • Cluster->Job

    • SlotManager 负责Slot的资源申请
  • Job->Task

    • SchedulerNG 负责Job调度引擎
    • 单个Slot可以用于一个或多个Task执行
    • 相同的Task不能在一个Slot中运行

Slot 计算资源管理

image-20210707160951674.png

  • TM有固定数量的 Slot 资源
  • Slot 数量由配置决定
  • Slot 资源由 TM 资源及 Slot 数量决定
  • 同一 TM 上的 Slot 之间无差别
组成部分配置参数描述
框架堆内存(Framework Heap Memory)taskmanager.memory.framework.heap.size用于 Flink 框架的 JVM 堆内存
任务堆内存(Task Heap Memory)taskmanager.memory.task.heap.size用于 Flink 应用的算子及用户代码的 JVM 堆内存。
托管内存(Managed memory)taskmanager.memory.managed.size taskmanager.memory.managed.fraction由 Flink 管理的用于排序、哈希表、缓存中间结果及 RocksDB State Backend 的本地内存。
框架堆外内存(Framework Off-heap Memory)taskmanager.memory.framework.off-heap.size用于 Flink 框架的堆外内存(直接内存或本地内存)
任务堆外内存(Task Off-heap Memory)taskmanager.memory.task.off-heap.size用于 Flink 应用的算计及用户代码的堆外内存(直接内存或本地内 存)。
网络内存(Network Memory)taskmanager.memory.network.min taskmanager.memory.network.max taskmanager.memory.network.fraction用于任务之间数据传输的直接内存(例如网络传输缓冲)。该内存部 分为基于 Flink总内存的受限的等比内存部分。
JVM Metaspacetaskmanager.memory.jvm-metaspace.sizeFlink JVM 进程的 Metaspace。
JVM 开销taskmanager.memory.jvm-overhead.min taskmanager.memory.jvm-overhead.max taskmanager.memory.jvm-overhead.fraction用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内 存部分为基于进程总内存的受限的等比内存部分。

Flink运维与指标监控

Metric 指标分类与采集

Metric 类型:
  • Counter

    计数器

  • Gauge

    最简单的Metric,反映一个值

  • Meter

    单位时间内发生事件的次数

  • Histogram

    统计数据分布,Mean,Max,Min,StdDec等

MetricGroup
  • Metric 在 Flink 内部有多层结构,以 Group 的方式组织
  • Metric 唯一标识:Metric Group + Metric Name

image-20210707161355207.png

获取 Metric

image-20210707161449665.png

指标采集来自

  • ResourceManager
  • Dispatcher
  • JobManager
  • TaskManager

Metric Reporter

指标的上报 report
  • Ganglia

  • Graphite

  • JMX

  • StatsD

  • InfluxDB

  • Prometheus

    Flink RestAPI

    主要功能

    • 系统监控指标

      • /jobmanager/metrics
      • /jobs/{jobid}/metrics
      • /jobs/{jobid}/vertices/{jobid}/metrics?get=0.numRecordsOutPerSecond 􏰀 /taskmanagers
      • /taskmanagers/{taskId}/metrics?get=
    • 任务管理

      • /jobs
      • /jobs/overview
      • /jobs/{jobid}
      • /jobs/{jobid}/cancel
    • 集群管理

      • /cluster
    • 配置信息

      • /config
      • /jobmanager/config
      • /jobs/{jobid}/checkpoints/config
      • /jobs/{jobid}/config
    • 资源上传(Jars)

      • /jars/{jarid}
      • /jars/upload
      • /jars
      • /jars/{jarid}/run

Flink Table Sql /API

现状和原因

image-20210708104036962.png

  • DataStream API/ Process Function 主要面向开发者

    • 具有 Java/Scala 开发经验
    • 需要对 Time,State 以及 Window 等流式概念具有非常深入的理解
    • 具有分布式处理的经验和知识
    • 具有作业调优经验
  • 更多数据分析人员和业务人员使用SQL

    • SQL 学习门槛相对较低,且普及率非常高
    • 技术发展趋势:大部分计算框架都提供 SQL 接口(Spark SQL)
    • 声明式语言规范
    • 基于规则对查询进行优化
    • 高效执行
    • 屏蔽底层复杂技术概念和实现
    • 实现批流一体,同时兼容批流处理

Flink SQL 的主要应用场景

  • 实时ETL(Continuous ETL) & 数据迁移(Data Import)
  • 实时数据可视化大盘与报表(Live Dashboards & Reports)
  • 即席查询(Ad-hoc Analytics) & 数据查找(Exploration)

Blink执行计划新老版本比较

  • Blink 将批处理作业视作流处理的一种特例。严格来说,Table 和 DataSet 之间不支持相互 转换,并且批处理作业也不会转换成 DataSet 程序而是转换成 DataStream 程序,流处理 作业也一样。
  • Blink 计划器不支持 BatchTableSource,而是使用有界的 StreamTableSource 来替代。
  • 旧计划器和 Blink 计划器中 FilterableTableSource 的实现是不兼容的。旧计划器会 将 PlannerExpression 下推至 FilterableTableSource,而 Blink 计划器则是将 Expression 下推。
  • 基于字符串的键值配置选项仅在 Blink 计划器中使用。
  • PlannerConfig 在两种计划器中的实现(CalciteConfig)是不同的。
  • Blink 计划器会将多 sink(multiple-sinks)优化成一张有向无环图(DAG), TableEnvironment 和 StreamTableEnvironment 都支持该特性。旧计划器总是将每个 sink都优化成一个新的有向无环图,且所有图相互独立。
  • 旧计划器目前不支持 catalog 统计数据,而 Blink 支持。

Flink Table API StreamTableEnvironment|TableEnvironment

核心功能
  • 在内部的 catalog 中注册 Table • 注册外部的 catalog
  • 加载可插拔模块
  • 执行SQL查询
  • 注册自定义函数 (scalar、table 或 aggregation)
  • 将 DataStream 或 DataSet 转换成 Table
  • 持有对 ExecutionEnvironment 或 StreamExecutionEnvironment 的引用
Table API
Table revenue = orders .filter($("cCountry").isEqual("FRANCE")) .groupBy($("cID"), $("cName") .select($("cID"), $("cName"), $("revenue").sum().as("revSum"));
Table SQL
# Sql Query select
Table revenue = tableEnv.sqlQuery( "SELECT cID, cName, SUM(revenue) AS revSum " + "FROM Orders " + "WHERE cCountry = 'FRANCE' " + "GROUP BY cID, cName" );
​
# Sql executer insert|update|delete
tableEnv.executeSql( "INSERT INTO RevenueFrance " + "SELECT cID, cName, SUM(revenue) AS revSum " + "FROM Orders " + "WHERE cCountry = 'FRANCE' " + "GROUP BY cID, cName" );
其他概念
  • 临时表与永久表

    根据元数据是否被持久化,分为临时表和永久表:

    • 临时表和 Session 生命周期一致,多 Session 不共享 Meta 信息。
    • 临时表存储在默认基于内存的 Catalog 中,持久表需要借助于外部 Catalog 支持(例如 Hive Catalog)来存储元数据。
    • 可以使用与已存在的永久表相同的标识符去注册临时表。
    • 临时表会屏蔽永久表,只要临时表存在,永久表就无法访问。
  • 数据类型与Table Schema

    • DataStream 和 DataSet APIs 数据类型:

      • 例如Tuple(Scala内置以及FlinkJavatuple)、POJO类型、Scalacaseclass类型以及Row 类型,基本数据类型等
      • Table底层默认的数据类型为Row
      • TableSchema映射就需要考虑如何将TableSchema转换成以上数据类型
    • 数据类型到 table schema 的映射有两种方式:

      • 基于字段位置
      • 基于字段名称
    • Flink 支持以下数据类型转换为 Table Schema

      • 原子类型
      • Tuple类型(Scala 和 Java)和 Case Class类型(仅 Scala) •
      • POJO 类型 (Java 和 Scala)
      • Row类型
  • 动态表 & 连续查询

    • 与表示批处理数据的静态表不同,动态表是随时间变化的。

    • 基于动态表查询

      • 查询动态表将生成一个连续查询 ;
      • 一个连续查询永远不会终止,结果会生成一个动态表;
      • 查询不断更新其(动态)结果表,以反映其(动态)输入表上的更改;
      • 动态表上的连续查询非常类似于定义物化视图的查询;
      • 连续查询的结果在语义上总是等价于以批处理模式在输入表快照上执行的相同查询的结果;
    • Temporal Aggregation

      • Group By Window Aggregation 统计计算每个小时中,每个用户的点击次数
      • Over Window Aggregation 计算每次点击之前两个小时内的点击总数
    • Join 连表查询

      • Join 类型

        INNERJOIN,[LEFT,RIGHT,FULL]OUTERJOIN,CROSSJOIN

      • Join 谓语

        Equality Predicates& Non-EqualityPredicates

      • Join 算法

        Nested-Loops,Index-Nested-Loops,Sort-Merge,Hybrid-Hash,..

      • Static tables are completely available when a join is processed

SQL Client 介绍使用

简介

image-20210708112019863.png

SQL 客户端配置
  • 启动Flink 集群

    • ./bin/start-cluster.sh
  • 启动 SQL Client:

    • ./bin/sql-client.sh embedded
    • SQL客户端将从./conf/sql-client-defaults.yaml中读取配置
  • 执行查询操作

    • SELECT 'HelloWorld';
CLI 可视化结果提供三种展示模式:
模式说明配置项
表格模式 (table mode)在内存中实体化结果,并将结果用规则 的分页表格可视化展示出来。SET execution.result- mode=table;
变更日志模式 (changelog mode )不会实体化和可视化结果,而是由插入 (+)和撤销(-)组成的持续查询产生 结果流。SET execution.result- mode=changelog;
Tableau模式 (tableau mode)更接近传统的数据库,会将执行的结果 以制表的形式直接打在屏幕之上。SET execution.result- mode=tableau;

Table & SQL Connectors

系统内置 TableConnector

名称版本SOURCESINKFileSystemBounded and Unbounded Scan, LookupStreaming Sink, Batch SinkEES6.x & 7.xNot supportedStreaming Sink, Batch SinkApache Kafka0.10+Unbounded ScanStreaming Sink, Batch SinkAmazon Kinesis Data StreamUnbounded ScanStreaming SinkJDBCBounded Scan, LookupStreaming Sink, Batch SinkApache Hbase1.4.x & 2.2.xBounded Scan, LookupStreaming Sink, Batch SinkApache HiveSupported VersionsUnbounded Scan, Bounded Scan, LookupStreaming Sink, Batch Sink

DDL 模式,支持 Flink SQL

image-20210708113040557.png

TableConnector 使用总结
Connector Table 注册:
  • TableName

    • 用于标记和获取唯一的 Table
  • TableConnector

    • 指定外部数据源,eg: Kafka,Elastic Search...
  • TableFormat

    • 数据源数据对应的格式,eg: Json,CSV,Parquet...
    • 可以自动生成,也可以自定义
  • TableSchema

    • 用于定义 Flink Table 系统中的 Schema
    • 新版本 API 支持 Watermark 和 Timestamp 定义
自定义 Table Connector

image-20210708113304723.png

Table Connector 描述

image-20210708113328260.png

TableFactory 定义

image-20210708113356596.png

TableConnector 服务发现

image-20210708113420408.png

Table Connector 组成

1.10版本以前

image-20210708113529868.png

1.11 版本重构后

image-20210708113846235.png

TableSource 接口定义

1.10 以前。实现TableSource

image-20210708113613523.png

1.11以后 实现ScanTableSource LoookupTableSource

image-20210708114012267.png