如何保证Exactly-Once语义
Flink通过实现"两阶段提交" 和 "状态保存" 来实现端到端的精准一致性语义。 分为以下几个步骤:
开始事务: 创建一个临时文件夹,来写把数据写入到这个文件夹里面;
预提交: 将内存中缓存的数据写入临时文件,并关闭;
正式提交:将之前写完的临时文件放入目标目录下。这代表着最终的数据会有一些延迟;
丢弃: 丢弃临时文件;
若失败发生在预提交成功后,正式提交前。可以根据状态来提交 预提交的数据,也可删除预提交的数据。
Flink中的Watermark机制
① Watermark 是一种衡量 Event Time 进展的机制,通常用来触发窗口计算,可以设定延迟触发;
② Watermark 是用于处理乱序事件的,而正确的处理乱序事件,通常用Watermark 机制结合 window 来实现;
③ 基于事件时间,用来触发窗口、定时器等;
④ watermark主要属性就是时间戳,反映的是事件发生的时间,而不是事件处理的时间;
⑤ watermark是单调不减的;
⑥ 数据流中的 Watermark 用于表示 timestamp 小于 Watermark 的数据,都已经到达了,如果后续还有timestamp 小于 Watermark 的数据到达,称为迟到数据。
Watermark是数据吗?怎么生成的?怎么传递的?
Watermark是一条携带时间戳的特殊数据,从代码指定生成的位置,插入到流里面。
一对多:广播
多对一:取最小
多对多:拆分来看,其实就是上面两种的结合
Watermark的生成方式?
间歇性:来一条数据,更新一次watermark
周期性:固定周期更新watermark
官方提供的api是基于周期的,默认200ms,因为间歇性会给系统带来压力。
Watermark=当前最大事件时间 - 乱序时间 - 1ms。 [ 左闭右开的原因 )
Checkpoint机制(检查点)(容错机制)
为了保证state(应用状态)容错,Flink提供了处理故障的措施,这种措施称之为checkpoint(一致性检查点)。checkpoint是Flink实现容错的核心功能,主要是周期性地触发checkpoint,将state生成快照持久化到外部存储系统(比如HDFS)。这样一来,如果Flink程序出现故障,那么就可以从上一次checkpoint中进行状态恢复,从而提供容错保障。
能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性。Flink的Checkpoint机制原理来自“Chandy-Lamport algorithm”算法(分布式快照)。
另外,通过checkpoint机制,Flink可以实现Exactly-once语义(Flink内部的Exactly-once,关于端到端的exactly_once,Flink是通过两阶段提交协议实现的)。
从检查点恢复数据:
第一步:重启作业
第二步:从上一次检查点恢复状态数据
第三步:继续处理新的数据
Flink是如何容错的?
Flink实现容错主要靠强大的CheckPoint机制和State机制。
Checkpoint负责定时制作分布式快照、对程序中的状态进行备份;
State 用来存储计算过程中的中间状态;
通过Checkpoint(基于ABS算法)实现容错,具体过程如下:
① JobManager定期向source发送Checkpoint的请求(由代码中的Checkpoint配置决定),向下游发送CheckpointBarrier;
② 每个计算算子收到CheckpointBarrier,进行Barrier对齐,对齐后存储Checkpoint到状态后端;
③ sink节点Barrier对齐后,多个sink确认下Checkpoint是否正常,如果正常JobManager确定本次Checkpoint结束;
④ 中途失败,只需要拿取前面的Checkpoint重新计算,进行恢复。
Flink中的Checkpoint和Spark中的Checkpoint区别
Flink中的Checkpoint主要作用是: 容错机制
Flink中的Checkpoint和Spark中的Checkpoint区别主要有2点:
① flink更轻量,可以根据时间戳更新state,
因为在Flink中Checkpoint是持久化全局的状态 state (keyed state 或 Operator state)的快照,在Flink中==增量==的快照,效率比较高。
对比 Spark :重量的快照,Spark每次全量的快照,Flink 每次增量的快照
spark是每个批次全量保存
② 在Flink中的Checkpoint中又仅一次语义概念和用法 ,而spark checkpoint没有仅一次的概念
③ 其次flink的 checkpoint有三个状态后端,memery、rocksdb、hdfs,所谓的状态后端就是checkpoint的存储位置,
在Spark中checkpoint的存储位置一般保存在HDFS,也可以保存至本地磁盘
但是一般情况下,checkpoint在Flink和Spark中保存的位置没太大区别,基本相同,因为生产情况下一般存HDFS。
状态机制
Flink中的状态机制(状态存储)?
① 什么是状态: 在flink在做计算的过程中,经常需要存储中间状态,像source,sink ;
② 状态的存储: 使用状态后端来管状态和checkpoint的存储 ;
③ 状态的一致性: 通过checkpoint对任务的状态进行快照,以故障恢复的时候保证状态的一致性。
Flink提供了三种状态存储方式(状态后端)
MemoryStateBackend (内存级的状态后端)
FsStateBackend (将checkpoint存到远程的持久化文件系统(FileSystem)上)
RocksDBStateBackend (将所有状态序列化后,存入本地的 RocksDB 中存储)
Flink处理迟到数据的几种方式?
方式1:设置水位线延迟时间。水位线延迟设置,一般设置为毫秒 到 秒级别;
方式2:允许窗口处理迟到数据。如:allowedLateness延长窗口的关闭时间;
方案3:迟到的数据放在侧输出流;
上面的两种方式,都是通过延迟水位线或者延长窗口的方式来处理的,实际处理过程中都会占据资源,不可能一直延迟水位线或者让窗口一直存在,在允许范围内的数据处理完毕之后,还得有一种兜底方案,处理极限情况,那就是直接把迟到的数据输出到侧输出流。
Flink的重启策略
- 固定延迟重启策略
- 故障率重启策略
- 没有重启策略
- Fallback重启策略(默认是这个Fallback重启策略)
Flink分区策略 (9种)
分区策略是用来决定数据如何发送至下游
- GlobalPartitioner (数据发到下游算子的第一个实例)
- ShufflePartitioner (数据随机分发到下游算子)
- RebalancePartitioner (数据循环发送到下游的实例)
- BroadcastPartitioner (输出到下游算子的每个实例中)
- ForwardPartitioner (上下游算子并行度一样)
- KeyGroupStreamPartitioner (按Key的Hash值输出到下游算子)
- CustomPartitionerWrapper (用户自定义分区器)
- BinaryHashPartitioner (对BinaryRowData这种数据进行hash分区)
- RescalePartitioner (根据上下游算子的并行度,循环输出到下游算子)
【实际使用在算子后加分区器】
Flink的序列化如何做
Apache Flink摒弃了Java原生的序列化方法,以独特的方式处理数据类型和序列化,包含自己的类型描述符,泛型类型提取和类型序列化框架。
TypeInformation 是所有类型描述符的基类。它揭示了该类型的一些基本属性,并且可以生成序列化器。TypeInformation 支持以下几种类型:
- BasicTypeInfo: 任意Java 基本类型或 String 类型
- BasicArrayTypeInfo: 任意Java基本类型数组或 String 数组
- WritableTypeInfo: 任意 Hadoop Writable 接口的实现类
- TupleTypeInfo: 任意的Flink Tuple 类型(支持Tuple1 to Tuple25)。Flink tuples 是固定长度固定类型的Java Tuple实现
- CaseClassTypeInfo: 任意的 Scala CaseClass(包括 Scala tuples)
- PojoTypeInfo: 任意的 POJO (Java or Scala),例如,Java对象的所有成员变量,要么是 public 修饰符定义,要么有 getter/setter方法。
- GenericTypeInfo: 任意无法匹配之前几种类型的类
针对前六种类型数据集,Flink皆可以自动生成对应的TypeSerializer,能非常高效地对数据集进行序列化和反序列化。
Flink中广播变量,使用时需要注意什么?
我们知道Flink是并行的,计算过程可能不在一个 Slot 中进行,那么有一种情况即:当我们需要访问同一份数据。那么Flink中的广播变量就是为了解决这种情况。
我们可以把广播变量理解为是一个公共的共享变量,我们可以把一个dataset 数据集广播出去,然后不同的task在节点上都能够获取到,这个数据在每个节点上只会存在一份。
Flink是如何处理反压的?
flink 内部是基于 producer-consumer 模型来进行消息传递的,flink的反压设计也是基于这个模型。flink 使用了高效有界的分布式阻塞队列,就像 Java 通用的阻塞队列(BlockingQueue)一样。下游消费者消费变慢,上游就会受到阻塞。
其中Flink Web UI 的反压监控提供了SubTask级别的反压监控。
Flink任务延迟高,想解决这个问题,你会如何入手?
在Flink的后台任务管理中,我们可以看到Flink的哪个算子和task出现了反压。最主要的手段是资源调优和算子调优。资源调优即是对作业中的Operator的并发数、CPU、堆内存 等参数进行调优。作业参数调优包括:并行度的设置,State的设置,checkpoint的设置。
Flink的并行度设置
Flink中的任务被分为多个并行任务来执行,其中每个并行的实例处理一部分数据。这些并行实例的数量被称为并行度。我们在实际生产环境中可以从四个不同层面设置并行度:
操作算子层面(Operator Level)
执行环境层面(Execution Environment Level)
客户端层面(Client Level)
系统层面(System Level)
需要注意的优先级:算子层面>环境层面>客户端层面>系统层面。
并行度的设置:一般设为kafka的分区数,达到1:1
遵循2的n次方:比如2、4、8、16…..
分布式快照的原理是什么?
Flink的分布式快照是根据Chandy-Lamport算法量身定做的。简单来说就是持续创建分布式数据流及其状态的一致快照。
核心思想是在 input source 端插入barrier,控制barrier的同步来实现snapshot的备份和exactly-once语义。
Flink内存模型
Join
双流join
在flink中,双流join主要分为2中类型:Join大体分类只有两种:Window Join和Interval Join
① Window Join又可以根据Window的类型细分出3种:
Tumbling Window Join、Sliding Window Join、Session Window Join。
Windows类型的join都是利用window的机制,先将数据缓存在Window State中,当窗口触发计算时,执行Join操作。
② Interval join也是利用state存储数据再处理,区别在于State中的数据有失效机制,依靠数据触发数据清理。
目前Stream Join的结果时数据笛卡尔积。
双流join与传统数据库join区别
数据集合:传统数据库左右两个表的数据集合是有限的,双流JOIN的数据会源源不断的流入;
结果更新: 传统数据库表JOIN是一次执行产生最终结果后退出,双流JOIN会持续不断的产生新的结果;
计算驱动: 双流JOIN由于左右两边的流的速度不一样,需要状态存储,双流驱动。比如:左边数据到来的时候右边数据还没有到来,或者右边数据到来的时候左边数据没有到来,所以在实现中要将左右两边的流数据进行保存,以保证JOIN的语义。
Flink的interval join的实现原理?join不上的怎么办?
底层调用的是keyby+connect ,处理逻辑:
1)判断是否迟到(迟到就不处理了)
2)每条流都存了一个Map类型的状态(key是时间戳,value是List存数据)
3)任一条流,来了一条数据,遍历对方的map状态,能匹配上就发往join方法
4)超过有效时间范围,会删除对应Map中的数据(不是clear,是remove)
Interval join不会处理join不上的数据,如果需要没join上的数据,可以用 coGroup+connect算子实现,或者直接使用flinksql里的left join或right join语法。