FLINK 总结

优点

支持高吞吐,低延时,高性能
支持事件时间,乱序数据
支持有状态的计算   累加,不用数据都完成在计算
高度灵活的窗口操作  
checkpoint 容错机制,Savepoints(保存点)
 

Flink架构

Flink运行时架构主要包括四个不同的组件,它们会在运行流处理应用程序时协同工作:
作业管理器(JobManager
)、
资源管理器(
ResourceManager)、
任务管理器(
TaskManager),
以及分发器(Dispatcher
)。
因为
Flink是用JavaScala实现的,所以所有组件都会运行在Java虚拟机上。

1)作业管理器(JobManager 控制一个应用程序执行的主进程,也就是说,每个应用程序都会被一个不同的JobManager所控制执行。

JobManager会先接收到要执行的应用程序,这个应用程序会包括:作业图(JobGraph)、逻辑数据流图(logical dataflow graph)和打包了所有的类、库和其它资源的JAR包。JobManager会把JobGraph转换成 一个物理层面的数据流图,这个图被叫做执行图”(ExecutionGraph),包含了所有可以并发执行的任 务。JobManager会向资源管理器(ResourceManager)请求执行任务必要的资源,也就是任务管理器(TaskManager)上的插槽(slot)。一旦它获取到了足够的资源,就会将执行图分发到真正运行它们的TaskManager上。而在运行过程中,JobManager会负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。

2)资源管理器(ResourceManager

主要负责管理任务管理器(TaskManager)的插槽(slot),TaskManger插槽是Flink中定义的处理资源单 元。Flink为不同的环境和资源管理工具提供了不同资源管理器,比如YARNMesosK8s,以及 standalone部署。当JobManager申请插槽资源时,ResourceManager会将有空闲插槽的TaskManager分配 给JobManager。如果   供 平台发起会话,以提供启动TaskManager进程的容器。另外,ResourceManager还负责终止空闲的 TaskManager,释放计算资源。

3)任务管理器(TaskManager

Flink中的工作进程。通常在Flink中会有多个TaskManager运行,每一个TaskManager都包含了一定数量的 插槽(slots)。插槽的数量限制了TaskManager能够执行的任务数量。启动之后,TaskManager会向资源 管理器注册它的插槽;收到资源管理器的指令后,TaskManager就会将一个或者多个插槽提供给 JobManager调用。JobManager就可以向插槽分配任务(tasks)来执行了。在执行过程中,一个 TaskManager可以跟其它运行同一应用程序的TaskManager交换数据。

4)分发器(Dispatcher

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

2、任务提交流程

上图是从一个较为高层级的视角,来看应用中各组件的交互协作。如果部署的集群环境不同(例如 YARNMesosKubernetesstandalone等),其中一些步骤可以被省略,或是有些组件会运行在同一个 JVM进程中。

具体地,如果我们将Flink集群部署到YARN上,那么就会有如下的提交流程:

 Flink任务提交后,ClientHDFS上传FlinkJar包和配置,之后向Yarn ResourceManager提交任务, ResourceManager分配Container资源并通知对应的NodeManager启动ApplicationMaster ApplicationMaster启动后加载FlinkJar包和配置构建环境,然后启动JobManager,之后 ApplicationMasterResourceManager申请资源启动TaskManagerResourceManager分配Container资源 后,由ApplicationMaster通知资源所在节点的NodeManager启动TaskManagerNodeManager加载Flink Jar包和配置构建环境并启动TaskManagerTaskManager启动后向JobManager发送心跳包,并等待 JobManager向其分配任务。

Flink的窗口

window是一种切割无限数据为有限块进行处理的手段。

Window是无限数据流处理的核心,Window将一个无限的stream拆分成有限大小的buckets桶,我们可 以在这些桶上做计算操作。

Window类型

Window可以分成两类:
计数窗口跟时间窗口和会话窗口()

计数与时间  中 还分别有滚动窗口与滑动窗口 

窗口内函数  agg (一条条运算) 与 全窗口函数(全窗口数据运算 )
,其他api 例如 移除器 一歪t  触发器 吹哥  计时器,窗口分流函数

CountWindow:按照指定的数据条数生成一个Window。
注意:
CountWindow 的 window_size 指的是相同 Key 的元素的个数,不是输入的所有元素的总数。

        滚动窗口  .countWindow(5)

        滑动窗口  window_size,一个是 sliding_size

        //每当某一个 key 的个数达到 2 的时候,触发计算,计算最近该 key 最近 10 个元素的内容
        
keyedStream.countWindow(10,2)

TimeWindow:按照时间生成Window

对于TimeWindow,可以根据窗口实现原理的不同分成三类:
滚动窗口(
Tumbling   Window
滑动窗口(Sliding Window)
会话窗口(
Session Window)。

1.滚动窗口 : 时间对其,窗口长度固定,没有重叠,
适合做BI统计(做每个时间段的聚合计算)。

keyBy(_._1).timeWindow(Time.seconds(15)).reduce((r1, r2) => (r1._1, r1._2.min(r2._2)))

2.滑动窗口: 时间对其,窗口长度固定,可以有重叠,
对最近一个时间段内的统计(求某接口最近5min的失败率来决定是否要报警)。
传入两个参数window_size
,一个是 sliding_size

keyBy(_._1).timeWindow(Time.seconds(15),  Time.seconds(5)

3.会话窗口(Session Window)时间无对齐,通过一个session间隔来配置,一段时间内没有数据就关闭会话

计算每个用户在活跃期间总共购买的商品数量,如果用 30秒没有活动则视为会话断开

.window(ProcessingTimeSessionWindows.withGap(Time.seconds(30)))

窗口函数

window    function   定义了要对窗口中收集的数据做的计算操作,主要可以分为两类:

增量聚合函数incremental aggregation functions 每条数据到来就进行计算,保持一个简单的状态。典型的增量聚合函数有ReduceFunction

AggregateFunction

全窗口函数full window functions

先把窗口所有数据收集起来,等到计算的时候会遍历所有数据。
ProcessWindowFunction 就是一个 全窗口函数。

其它可选API

 

trigger() —— 触发器定义  window  什么时候关闭,触发计算并输出结果
evitor() —— 移除器 定义移除某些数据的逻辑
allowedLateness() —— 允许处理迟到的数据

sideOutputLateData() —— 将迟到的数据放入侧输出流
getSideOutput() —— 获取侧输出流

Window 的实现

数据进入窗口函数时,会先由Windowassigner,分配决定进入那个窗口,也有可能创建新窗口进入数据,或者同时进入过个窗口,

窗口数据是存储在state中 ,key/value   key是Window(标识符与元数据),value是元素集合
每个窗口都有属于自己Trigger Trigger 内有定时器用来决定一个窗口何时能够被计算或 清除。每当有元素加入到该窗口,或者之前注册的定时器超时了,那么Trigger都会被调用。Trigger的返 回结果可以是 continue(不做任何操作),fire(处理窗口数据),purge(移除窗口和窗口中的数 据),或者 fire + purge。一个Trigger的调用结果只是fire的话,那么会计算窗口并保留窗口原样,也就 是说窗口中的数据仍然保留不变,等待下次Trigger fire的时候再次执行计算。一个窗口可以被重复计算 多次知道它被  purge   了。在purge之前,窗口会一直占用着内存。

Trigger fire了,窗口中的元素集合就会交给 Evictor (如果指定了的话)。Evictor 主要用来遍历窗口 中的元素列表,并决定最先进入窗口的多少个元素需要被移除。剩余的元素会交给用户指定的函数进行 窗口的计算。如果没有  Evictor   的话,窗口中的所有元素会一起交给函数进行计算。

计算函数收到了窗口的元素(可能经过了 Evictor 的过滤),并计算出窗口的结果值,并发送给下游。 窗口的结果值可以是一个也可以是多个。DataStream  API  上可以接收不同类型的计算函数,包括预定义 的 sum() , min() , max() ,还有 ReduceFunction FoldFunction ,还有 WindowFunction WindowFunction      是最通用的计算函数,其他的预定义的函数基本都是基于该函数实现的。

Flink 对于一些聚合类的窗口计算(如sum,min)做了优化 ,每次不用保留所有数据,只需要保存一个result值,每次累加或者替换result值就可以了,大大减少内存提升性能,但是如果用户定义了 Evictor,则不会启用对聚合 窗口的优化,因为 Evictor 需要遍历窗口中的所有元素,必须要将窗口中所有元素都存下来。

时间语义

eventtime          信息事件创建时间
ingestiontime     事件进入flink Source时间,摄取时间
processingtime  算子时间处理时间

Flinkwatermark(水位线),watermark需要实现哪个实现类

水位线是为了解决 Eventtime 乱序问题,数据顺序到达flink 不一致

Watermark是一种衡量Event   Time进展的机制。

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

有序流的Watermarker如下图所示:(Watermark设置为0

Watermark的使用

Watermark的两种生成方式

1SourceFunction中产生,将Timestamp的分配(也就是上文提到的离散化)watermark的生成放在上 游,同时sourceFunction中也有两个方法生成watermark

通过collectwithTimestamp方法发送数据,和调用emitWatermark产生watermark,我们可以看到,调用 collectwithTimestamp需要传入两个参数,第一个参数就是数据,第二次参数就是数据对应的时间戳,这 样就完成了timestamp的分配,调用emitWatermark生成watermark

2)DataStream API中生成Watermark DataStream API中使用的TimestampAssigner 接口定义了时间戳的提取行为,其有两个不同接口 AssignerWithPeriodicWatermarks和AssignerWithPunctuatedWatermarks,分别代表了不同的Watermark生 成策略。TimestampAssigner接口体系如下图所示。

AssignerWithPeriodicWatermarks 是周期性生成Watermark策略的顶层抽象接口,该接口的实现类 周期性地生成Watermark,而不会针对每一个事件都生成。 AssignerWithPunctuatedWatermarks 对每一个事件都会尝试进行Watermark的生成,但是如果生成 的Watermark是null或者Watermark小于之前的Watermark,则该Watermark不会发往下游,因为发往下游 也不会有任何效果,不会触发任何窗口的执行。

 Flink SQL Watermark生成

3类Watermark生成策略。

1)周期性Watermark策略  作 PeriodicWatermarkAssigner ,周期性(一定时间间隔或者达到 一定的记录条数)地产生一个Watermark。

(1)AscendingTimestamps:递增Watermark,作用在Flink SQL中的Rowtime属性上,Watermark=当前收到的数据元素的最大时间戳-1,此处减1的目的是确保有最大时间戳的事件不会被当做迟到数据丢弃。
(2)BoundedOutOfOrderTimestamps:固定延迟Watermark,作用在Flink SQL的Rowtime属性上, Watermark=当前收到的数据元素的最大时间戳-固定延迟。

2)每事件Watermark策略  PreserveWatermark 每个eventtime都生产水位线  对时效性要求高的选择

3)无为策略   PreserveWatermark  不设定水位线

多流的Watermark

Watermark在作业的DAG从上游向下游传递,算子收到上游Watermark后会更新其Watermark。如果新的 Watermark大于算子的当前Watermark,则更新算子的Watermark为新Watermark,并将之前的Watermark发送给下游算子。

多流 输入会被分解为多个双流输入,所以对于多流Watermark的处理也就是双流Watermark的处理,无论是哪 一个流的Watermark进入算子,都需要跟另一个流的当前算子进行比较,选择较小的Watermark,即 Min(input1Watermark,intput2Watermark),与算子当前的Watermark比较,如果大于算子当前的 Watermark,则更新算子的Watermark为新的Watermark,并发送给下游,

在上图中,Source算子产生各自的Watermark,并随着数据流流向下游的map算子,map算子是无状态计 算,所以会将Watermark向下透传。window算子收到上游两个输入的Watermark后,选择其中较小的一 个发送给下游,window(1)算子比较Watermark 29和Watermark 14,选择Watermark 14作为算子当前 Watermark,并将Watermark 14发往下游,window(2)算子也采用相同的逻辑。

Flink解决数据延迟的问题

1、官方解释
迟到数据是指系统的事件时间时钟(由水印指示)在经过延迟元素时间戳之后的时间到达的元素。
2、解决方案
迟到数据可以说是一种特殊的乱序数据,它没有被watermark和Window机制处理,因为是在窗口关闭后才到达的数据
一般这种情况有三种处理办法:
1.重新激活已经关闭的窗口并重新计算以修正结果。
2.将迟到数据收集起来另外处理。
3.将迟到数据视为错误消息并丢弃。(默认方法)

车开走了

sideOutput 机制   迟到事件单独放入一个数据流,坐下一趟车,开快点追上来
allowedLateness机制    设置一个允许的最大迟到时长,等等你

Flink的Checkpoint机制

Checkpoint  :某一时刻,Flink中所有的Operator的当前State的全局快照,一般存在磁盘上。

Flink提供了Exactly once特性,是依赖于带有barrier的分布式快照+可部分重发的数据源功能实现的。 分布式快照中,就保存了operator的状态信息。

Flink的失败恢复依赖于检查点机制    +可部分重发的数据源

检查点机制机制checkpoint定期触发,产生快照,快照中记录了:当前检查点开始时数据源(例如Kafka)中消息的Offset 记录了所有有状态的operator当前的状态信息(例如sum中的数值)。

可部分重发的数据源Flink选择最近完成的检查点K,然后系统重放整个分布式的数据流,然后给予每个operator他们在检查点k快照中的状态。数据源被设置为从位置Sk开始重新读取流。例如在Apache Kafka中,那意味着告诉消费者从偏移量Sk开始重新消费。

Checkpoint 流程

第一步Checkpoint Coordinator 向所有 source 节点 trigger Checkpoint

第二步source 节点向下游广播 barrier下游的 task 只有收到所有 input barrier 才会执行相应的 Checkpoint

第三步,当 task 完成 state 备份后,会将备份数据的地址(state handle)通知给 Checkpoint写入到 存储的过程是异步的,意味着Flink程序在checkpoint运行的同时还可以处理数据。

第四步,下游的 sink 节点收集齐上游两个 input barrier 之后,会执行本地快照,这里特地展示了 RocksDB incremental Checkpoint 的流程,首先 RocksDB 会全量刷数据到磁盘上(红色大三角表示), 然后   Flink  框架会从中选择没有上传的文件进行持久化备份(紫色小三角)。

同样的sink 节点在完成自己的 Checkpoint 之后,会 state handle 返回通知 Coordinator

最后,当 Checkpoint coordinator 收集齐所有 task state handle,就认为这一次的 Checkpoint 全局完 成了,向持久化存储中再备份一个 Checkpoint meta 文件。

Flink Checkpoint的作用

Checkpoint  :某一时刻,Flink中所有的Operator的当前State的全局快照,一般存在磁盘上。

CheckPoint是通过快照(SnapShot)的方式使得将历史某些时刻的状态保存下来,当Flink任务意外挂掉 之后,重新运行程序后默认从最近一次保存的完整快照处进行恢复任务。

Flink中的Checkpoint底层使用了 Chandy-Lamport algorithm 分布式快照算法,可以保证数据的在分 布式环境下的一致性。

FlinkCheckpoint超时原因

1计算量大,CPU密集性,导致TM内线程一直在processElement,而没有时间做Checkpoint

解决方案:过滤掉部分数据,增大并行度

修改实现逻辑,进行批流分开计算,比如离线数据每半个小时进行一次计算,而实时计算只需要计算最 近半小时内的数据即可。总之两个方法,一、减少源数据量,过滤黑名单或者非法IDwindow聚合; 二、简化处理逻辑,特别是减少遍历。
2)数据倾斜

解决方案: 第一,两阶段聚合;第二,重新设置并行度,改变KeyGroup的分布
3频繁FULL GC‘Checkpoint Duration (Async)’时间长) StateSize达到200M以上,Async的时间会超过1min    这种情况比较少见。
4)出现反压

包含barrierevent buVer一直不到 subTaskCheckpointCoordinator做不了Checkpoint,就会超时。

Flink的Exactly Once语义怎么保证?

下级存储支持事务:Flink可以通过实现两阶段提交和状态保存来实现端到端的一致性语义。 分为以下几个步骤: TwoPhaseCommitSinkFunction
1)开始事务(beginTransaction)创建一个临时文件夹,来写把数据写入到这个文件夹里面
2)预提交(preCommit)将内存中缓存的数据写入文件并关闭
3)正式提交(commit)将之前写完的临时文件放入目标目录下。这代表着最终的数据会有一些延迟
4)丢弃(abort)丢弃临时文件
5)若失败发生在预提交成功后,正式提交前。可以根据状态来提交预提交的数据,也可删除预提交的 数据。

下级存储不支持事务: 具体实现是幂等写入,需要下级存储具有幂等性写入特性。

Exactly Once语义怎么保证

在分布式系统中,协调提交和回滚的通用做法是两阶段提交。Flink的TwoPhaseCommitSinkFunction使 用两阶段提交协议来保证端到端的Exactly-Once语义。

在checkpoint开始的时候,即两阶段提交中的预提交阶段。
首先,Flink的JobManager在数据流中注入一 个checkpoint屏障(它将数据流中的记录分割开)。 屏障通过operator传递。它将触发operator的状态快照写入到 状态后端。
data source保存了Kafka的offset, 之后把checkpoint 屏障传递到后续的operator
第二个operator中window聚合算出来的sum。当一个进程有它的内部状态的时候,除了在 checkpoint之前将需要将数据更改写入到state backend,不需要在预提交阶段做其他的动作。在 checkpoint成功的时候,Flink会正确的提交这些写入,在checkpoint失败的时候会终止提交。
data sink有外部的状态。因此,在预提交阶段,除了将状态 写入到state backend之外,data sink必须预提交自己的外部事务。 当checkpoint屏障在所有operator中都传递了一遍,以及它触发的快照写入完成,预提交阶段结束。
这个 时候,快照成功结束,整个程序的状态,包括预提交的外部状态是一致的。万一出错的时候,我们可以 通过checkpoint重新初始化。 下一步是通知所有operator,checkpoint已经成功了。这时两阶段提交中的提交阶段,Jobmanager为程 序中的每一个operator发起checkpoint已经完成的回调。
data source和window operator没有外部的状 态,在提交阶段中,这些operator不会执行任何动作。data sink拥有外部状态,所以通过事务提交外部 写入。 

对上述的知识点汇总一下:
一旦所有的operator完成预提交,就提交一个commit。
如果至少有一个预提交失败,其他的都会失败,这时回滚到上一个checkpoint保存的位置。
预提交成功后,提交的commit也需要保障最终成功-operator和外部系统需要提供这个保障。
如果 commit失败了(比如网络中断引起的故障),整个flink程序也因此失败,它会根据用户的重启策 略重启,可能还会有一个尝试性的提交。这个过程非常严苛,因为如果提交没有最终生效,会导致 数据丢失。
因此,我们可以确定所有的operator同意checkpoint的最终结果:要么都同意提交数据,要么提交被终止 然后回滚

Flink任务如何实现端到端一致?

Source端:数据从上游进入Flink,必须保证消息严格一次消费。同时Source 端必须满足可重放 (replay)。否则 Flink 计算层收到消息后未计算,却发生 failure 而重启,消息就会丢失。
Flink计算层:利用 Checkpoint 机制,把状态数据定期持久化存储下来,Flink程序一旦发生故障的时 候,可以选择状态点恢复,避免数据的丢失、重复。
Sink端:Flink将处理完的数据发送到Sink端时,通过 两阶段提交协议 ,即 TwoPhaseCommitSinkFunction 函数。该 SinkFunction 提取并封装了两阶段提交协议中的公共逻辑,保证Flink 发送Sink端时实现严格一次处理语义。
同时:Sink端必须支持事务机制,能够进行数据回滚或 者满足幂等性。
回滚机制:即当作业失败后,能够将部分写入的结果回滚到之前写入的状态。
幂等性:就是一个相同的操作,无论重复多少次,造成的结果和只操作一次相等。即当作业失败后,写 入部分结果,但是当重新写入全部结果时,不会带来负面结果,重复写入不会带来错误结果。

checkpoint 保存的位置

对于状态占用空间比较小的应用,快照产生过程非常轻量,高频率创建且对 Flink任务性能影响相对较小。如果状态占用较大,可以将检查点开启时间间隔调大。

checkpoint过程中状态数据一般被保存在一个可配置的环境中,通常是在 JobManager节点或HDFS上 rockdb

1Checkpoint开启和时间间隔指定

默认情况下Flink不开启检查点的,开启检查点并且指定检查点时间间隔为1000ms,根据实际情况自行选择,如果状态比较大,则建议适当 增加该值。    env.enableCheckpointing(1000);

2exactly-anceat-least-once语义选择

exactly-once语义保证整个应用内端到端的数据一致性,这种情况比较适合于数据要求比较高, 不允许出现丢数据或者数据重复
at-least-once语义更适合于延时和吞吐量要求非常高但对数据的一致性要求不高的场景
通过setCheckpointingMode()
方法来设定语义模式,默认exactly-once模式。

env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);

3Checkpoint超时时间env.getCheckpointConfig().setCheckpointTimeout(60000);
4)检查点之间最小时间间隔  .setMinPauseBetweenCheckpoints(500);
5)最大并行执行的检查点数量默认一个 .
setMaxConcurrentCheckpoints(1);
6)数据持久化 检查点

enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);

7failOnCheckpointingErrors

failOnCheckpointingErrors参数决定了当Checkpoint执行过程中如果出现失败或者错误时,任务是否同时 被关闭,默认值为True

barrier

快照机制的实现有主要两个部分组成,一个是屏障(Barrier),一个是状态(State)。

barrier将数据流中的记录隔离成一系列的记录集合,并将 一些集合中的数据加入到当前的快照中,快照在数据中会有多个,快照的状态是并行发生的

单流barrier  

Checkpoint  barrier  数据插入数据流中说明快照所包含的 数据在数据源中最大位置barrier  向下流动,当中间的操作算子收到上游所有同一个barrier 时,他会为快照发出barriers,进入其所有输出流中。当最后sink 算子 接收到上游所有的barrier,他会向checkpoint协调器确认快照完成。在所有sink确认快照后,意味快照着已完成。

多流barrier  n  m  对齐机制

当中间的操作算子收到上游 n输入流 barrier 时,它就不能处理来自该流的任何记录,直到它从其 他输入接收到barriers  n为止。不处理数据,但是会缓存数据。直到另一个输入流m的barrier 到达时才会优先处理缓存的数据。

Flink backPressure反压机制

现象 处理速度慢,接收速度快,系统处理不了接收的数据

原因 垃圾回收卡顿可 能导致流入的数据堆积起来,或者数据源可能出现发送数据过快的峰值。

后果 如果处理不当,背压会导致资 源耗尽,甚至导致数据丢失。

其中有会一个问题就是下游接收数据时,本地缓存用完了下游任务的底层接收缓存占满数据,会停止读取数据,这样上游数据发送不了数据,就会慢慢积压数据,从而背压到发送端,形成对上又所有任务的背压

为了解决上节的单任务背压影响全链路的问题,在Flink 1.5之后,引入了Credit-based Flow Control,基于信用点的流量控制


子任务的本地缓存分为两个部分,独占缓存 ,与浮动缓存
下游任务会发送独占缓存大小(信用点)给上游,当缓存为0时,将不在发送数据给下游,起到背压的作用
信息在发送数据事
在发送数据的同时,发送方还会把队列中暂存排队的 数据量发给接收方,接收方收到后,根据本地缓存的大小,决定是否去浮动缓存里请求更多的缓存来加 速队列的处理,起到动态控制流量的作用。

通过这样的设计,就实现了任务级别的背压:任意一个任务产生背压,只会影响这个任务,并不会对 TaskManger上的其它任务造成影响。

Flink状态机制
 

按照数据的划分和扩张方式,Flink中状态 (State) 大致分为2类:
Keyed States( 键控状态 )
Operator States( 算子状态 )


Keyed States是一种特殊的算子状态,即状态是根据 key 值进行区分的,Flink会为每类键值维护一个状态实例。需要注意的是键控状态只能在 KeyedStream 上进行使用,我们可以通过 stream.keyBy(...) 来得到 KeyedStream 。

Flink 提供了以下数据格式来管理和存储键控状态(Keyed State):
        ValueState:存储单值类型的状态。可以使用 update(T) 进行更新,并通过 Tvalue() 进行检 索。
        ListState:存储列表类型的状态。可以使用 add(T) 或 addAll(List) 添加元素;并通过 get() 获得整个列表。
        ReducingState:用于存储经过 ReduceFunction 计算后的结果,使用 add(T) 增加元素。    
        AggregatingState:用于存储经过 AggregatingState 计算后的结果,使用 add(IN) 添加元素。        
        MapState:维护 Map 类型的状态。


Operator State,状态是和算子进行绑定的,一个算子的状态不能被其他算子所访问到。

Operator States的动态扩展是非常灵活的,现提供了3种扩展( 支持的存储类型 ):
        ListState:并发度在改变的时候,会将并发上的每个List都取出,然后把这些List合并到一个新的 List,然后根据元素的个数在均匀分配给新的Task;( 存储列表类型的状态 )
        UnionListState:相比于ListState更加灵活,把划分的方式交给用户去做,当改变并发的时,会将 原来的List拼接起来。然后不做划分,直接交给用户, 具体的划分行为则由用户进行定义
        BroadcastState:如大表和小表做Join时,小表可以直接广播给大表的分区,在每个并发上的数据 都是完全一致的。做的更新也相同,当改变并发的时候,把这些数据copy到新的Task即可。( 用 于广播的算子状态 )


在一个operator上,可能会有很多个key,从而对应多个keyed state

举例来说,Flink中的Kafka Connector,就使用了operator state。它会在每个connector实例中,保存该实 例中消费topic的所有(partition, Offset)映射 (Keyed State)

KeyedStream流上的每一个key,都对应一个state

Flink广播流

广播状态(Broadcast State)是 Apache Flink 中支持的第三种类型的Operator State。
Broadcast State使 得 Flink 用户能够以容错、一致、可扩缩容地将来自广播的低吞吐的事件流数据存储下来,被广播到某 个 operator 的所有并发实例中,然后与另一条流数据连接进行计算。
广播状态与其他 operator state 之间有三个主要区别:
        Map 的格式
        有一条广播的输入流 operator
        可以有多个不同名字的广播状态

缺点
1)使用广播状态,operator task 之间不会相互通信   不同算子 ,数据可能不一致
2)广播状态中事件的顺序在各个并发实例中可能不尽相同    顺序可能不同
3)所有 operator task 都会快照下他们的广播状态     占用空间
4)RocksDBStateBackend状态后端目前还不支持广播状态         

        

State对象存储位置 Flink广播流

三种存储方式:
HeapStateBackend
MemoryStateBackend、堆内存中  jobmanager
FsStateBackend、hdfs 
RockDBStateBackend。 本地RocksDB,需要配置一个远端的filesystem

savepoint和checkpoint区别


checkpoint的侧重点是“容错”,即Flink作业意外失败并重启之后,能够直接从早先打下的checkpoint恢复运行,且不影响作业逻辑的准确性。
而savepoint的侧重点是“维护”,即Flink作业需要在人工干预下
手动重启、升级、迁移或A/B测试时,先将状态整体写入可靠存储,维护完毕之后 再从savepoint恢复现场。

savepoint通过checkpoint机制创建的所以savepoint本质上是特殊的checkpoint

checkpoint是支持增量的(通过RocksDB),特别是对于超大状态的作业而言可以降低写入成本。
savepoint并不会连续自动触发,所以savepoint没有必要支持增量。

用Flink

一般是双流join或者和维度表join,进行逻辑计算,之后写出到数据库。
下面是一个案例: 事实表通常存储在kafka中,维表通常存储在外部设备中(比如MySQL,HBase)。对于每条流式数据, 可以关联一个外部维表数据源,为实时计算提供数据关联查询。维表可能是会不断变化的,在维表join 时,需指明这条记录关联维表快照的时刻。需要注意是,目前Flink SQL的维表join仅支持对当前时刻维表快照的关联(处理时间语义),而不支持事实表rowtime所对应的的维表快照。

1、维度表join

1)预加载维表

将维表全量预加载到内存里去做关联,具体的实现方式就是我们定义一个类,去实现 RichFlatMapFunction,然后在 open 函数中读取维度数据库,再将数据全量的加载到内存,然后在 probe 流上使用算子 ,运行时与内存维度数据做关联。 这个方案的优点就是实现起来比较简单,缺点也比较明显,因为我们要把每个维度数据都加载到内存里 面,所以它只支持少量的维度数据。同时如果我们要去更新维表的话,还需要重启作业,所以它在维度 数据的更新方面代价是有点高的,而且会造成一段时间的延迟。对于预加载维表来说,它适用的场景就 是小维表,变更频率诉求不是很高,且对于变更的及时性的要求也比较低的这种场景。

改进:open()新建一个线程定时加载维表,这样就不需要人工的去重启 Job 来让维度数据做更新,可以 实现一个周期性的维度数据的更新。

2)distributed

cache 通过 Distributed cash 的机制去分发本地的维度文件到 Task Manager后再加载到内存做关联。实现方式 可以分为三步: 通过 env.registerCached 注册文件。 实现 RichFunction,在 open 函数里面通过 RuntimeContext 来获取 Cache 文件。 解析和使用这部分文件数据。 因为数据要加载到内存中,所以支持的数据量比较小。而且如果维度数据需要更新,也是需要重启作业 的。 那么它适用的场景就是维度数据是文件形式的、数据量比较小、并且更新的频率也比较低的一些场景, 比如说我们读一个静态的码表、配置文件等等。

3)热存储关联

把维度数据导入到像 Redis、Tair、HBase 这样的一些热存储中,然后通过异步 IO 去查询,并且叠加使 用 Cache 机制,还可以加一些淘汰的机制,最后将维度数据缓存在内存里,来减轻整体对热存储的访问 压力。 如上图展示的这样的一个流程。在 Cache 这块的话,比较推荐谷歌的 Guava Cache,它封装了一些关于 Cache 的一些异步的交互,还有 Cache 淘汰的一些机制,用起来是比较方便的。 异步 IO 可以并行发出多个请求,整个吞吐是比较高的,延迟会相对低很多。如果使用异步 IO 的话,它 对于外部存储的吞吐量上升以后,会使得外部存储有比较大的压力,有时也会成为我们整个数据处理上 延迟的瓶颈。所以引入 Cache 机制是希望通过 Cache来去减少我们对外部存储的访问量。 这个方案的优点就是维度数据不用全量加载到内存中,不受限于内存大小。但是需要依赖热存储资源, 再加上cache过期时间,所以最后结果会有一定的延迟。适用于维度数据量比较大,能接受维度更新有 一定延迟的情况。

4)广播维表

利用 Broadcast State 将维度数据流广播到下游 Task 做 Join。 将维度数据发送到 Kafka 作为广播原始流 S1 定义状态描述符 MapStateDescriptor。调用 S1.broadcast(),获得 broadCastStream S2 调用非广播流 S3.connect(S2),得到 BroadcastConnectedStream S4 在 KeyedBroadcastProcessFunction/BroadcastProcessFunction 实现关联处理逻辑,并作为参数调用 S4.process() 广播维表维度的变更可以及时的更新到结果,但是数据还是需要保存在内存中,因为它是存在 State 里 的,所以支持维表数据量仍然不是很大。适用的场景就是我们需要时时的去感知维度的变更,且维度数 据又可以转化为实时流。

5)Temporal table function join

首先说明一下什么是 Temporal table?它其实是一个概念:就是能够返回持续变化表的某一时刻数据内 容的视图,持续变化表也就是 Changingtable,可以是一个实时的 Changelog 的数据,也可以是放在外部 存储上的一个物化的维表。 它的实现是通过 UDTF 去做 probe 流和 Temporal table 的 join,称之 Temporal table function join。这种 Join 的方式,它适用的场景是维度数据为 Changelog 流的形式,而且我们有需要按时间版本去关联的诉 求。 在 Changelog 流上面去定义 TemporalTableFunction,这里面有两个关键的参数是必要的。第1个参数就 是能够帮我们去识别版本信息的一个 Time attribute,第 2 个参数是需要去做关联的组件。 在 tableEnv 里面去注册 TemporalTableFunction 的名字。

维表join方案对比

 2、双流join

 批处理有两种方式处理两个表的Join,

一种是基于排序的Sort-Merge Join,另一种是转化为Hash Table 加载到内存里做Hash Join。

在双流Join的场景中,Join的对象是两个流,数据是不断进入的,所以我们Join的结果也是需要持续更 新的。基本思路是将一个无线的数据流,尽可能拆分成有限数据集去做Join。

1)Regular Join 这种 Join 方式需要去保留两个流的状态,持续性地保留并且不会去做清除。两边的数据对于对方的流 都是所有可见的,所以数据就需要持续性的存在 State 里面,那么 State 又不能存的过大,因此这个场景 的只适合有界数据流。

2)Interval Join 加入了一个时间窗口的限定,要求在两个流做 Join 的时候,其中一个流必须落在另一个流的时间戳的 一定时间范围内,并且它们的 Join key 相同才能够完成 Join。加入了时间窗口的限定,就使得我们可以 对超出时间范围的数据做一个清理,这样的话就不需要去保留全量的 State。 Interval Join 是同时支持 processing time 和 even time去定义时间的。如果使用的是 processing time, Flink 内部会使用系统时间去划分窗口,并且去做相关的 state 清理。如果使用 even time 就会利用 Watermark 的机制去划分窗口,并且做 State 清理。

3)Window join 将两个流中有相同 key 和处在相同 window 里的元素去做 Join。它的执行的逻辑比较像 Inner Join,必 须同时满足 Join key 相同,而且在同一个 Window 里元素才能够在最终结果中输出。

Flink SQL解析过程

Flink内部的SQL引擎模块就是基于Calcite。

一条SQL从提交到卡奥赛特解析,优化,到最后的Flink执行,一般分以下过程:

1)Sql Parser: 将sql语句通过java cc解析成AST(语法树),在calcite中用SqlNode表示AST; 2)Sql Validator: 结合数字字典(catalog)去验证sql语法;
3)生成Logical Plan: 将sqlNode表示的AST转换成逻辑计划, 用relNode表示;
4)生成 optimized LogicalPlan: 先基于calcite 规则去优化逻辑计划,基于flink定制的一些优化rules 去优化logical Plan;
5)生成Flink PhysicalPlan: 这里也是基于flink里头的规则,将optimized LogicalPlan转成成Flink的物理执行计划;
6)将物理执行计划转成Flink ExecutionPlan:就是调用相应的tanslateToPlan方法转换和利用CodeGen元 编程成Flink的各种算子。

CEP概述


复合事件处理(Complex Event ProcessingCEP)是一种基于动态环境中事件流的分析技术,事件在这 里通常是有意义的状态变化,通过分析事件间的关系,利用过滤、关联、聚合等技术,根据事件间的时 序关系和聚合关系制定检测规则,持续地从事件流中查询出符合要求的事件序列,最终分析得到更复杂 的复合事件。
特征 目标:从有序的简单事件流中发现一些高阶特征;输入:一个或多个简单事件构成的事件流处理:识别简单事件之间的内在联系,多个符合一定规则的简单事件构成复杂事件输出:满足规则的复杂事件。

3、功能

CEP可以帮助在复杂的、不相关的时间流中找出 有意义的模式和复杂的关系,以接近实时或准实时的获得通知或组织一些行为

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值